@object-ui/plugin-calendar 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
- (function(_,p){typeof exports=="object"&&typeof module<"u"?p(exports,require("react"),require("@object-ui/core"),require("lucide-react"),require("@object-ui/components")):typeof define=="function"&&define.amd?define(["exports","react","@object-ui/core","lucide-react","@object-ui/components"],p):(_=typeof globalThis<"u"?globalThis:_||self,p(_.ObjectUIPluginCalendar={},_.React,_.ObjectUICore,_.LucideReact,_.ObjectUIComponents))})(this,(function(_,p,ee,te,N){"use strict";function ue(e){const l=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(e){for(const n in e)if(n!=="default"){const f=Object.getOwnPropertyDescriptor(e,n);Object.defineProperty(l,n,f.get?f:{enumerable:!0,get:()=>e[n]})}}return l.default=e,Object.freeze(l)}const re=ue(p);var U={exports:{}},Y={};var ne;function fe(){if(ne)return Y;ne=1;var e=Symbol.for("react.transitional.element"),l=Symbol.for("react.fragment");function n(f,g,c){var m=null;if(c!==void 0&&(m=""+c),g.key!==void 0&&(m=""+g.key),"key"in g){c={};for(var a in g)a!=="key"&&(c[a]=g[a])}else c=g;return g=c.ref,{$$typeof:e,type:f,key:m,ref:g!==void 0?g:null,props:c}}return Y.Fragment=l,Y.jsx=n,Y.jsxs=n,Y}var $={};var ae;function me(){return ae||(ae=1,process.env.NODE_ENV!=="production"&&(function(){function e(t){if(t==null)return null;if(typeof t=="function")return t.$$typeof===b?null:t.displayName||t.name||null;if(typeof t=="string")return t;switch(t){case M:return"Fragment";case C:return"Profiler";case s:return"StrictMode";case X:return"Suspense";case q:return"SuspenseList";case D:return"Activity"}if(typeof t=="object")switch(typeof t.tag=="number"&&console.error("Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."),t.$$typeof){case A:return"Portal";case I:return t.displayName||"Context";case P:return(t._context.displayName||"Context")+".Consumer";case R:var d=t.render;return t=t.displayName,t||(t=d.displayName||d.name||"",t=t!==""?"ForwardRef("+t+")":"ForwardRef"),t;case Q:return d=t.displayName||null,d!==null?d:e(t.type)||"Memo";case W:d=t._payload,t=t._init;try{return e(t(d))}catch{}}return null}function l(t){return""+t}function n(t){try{l(t);var d=!1}catch{d=!0}if(d){d=console;var h=d.error,y=typeof Symbol=="function"&&Symbol.toStringTag&&t[Symbol.toStringTag]||t.constructor.name||"Object";return h.call(d,"The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",y),l(t)}}function f(t){if(t===M)return"<>";if(typeof t=="object"&&t!==null&&t.$$typeof===W)return"<...>";try{var d=e(t);return d?"<"+d+">":"<...>"}catch{return"<...>"}}function g(){var t=x.A;return t===null?null:t.getOwner()}function c(){return Error("react-stack-top-frame")}function m(t){if(k.call(t,"key")){var d=Object.getOwnPropertyDescriptor(t,"key").get;if(d&&d.isReactWarning)return!1}return t.key!==void 0}function a(t,d){function h(){O||(O=!0,console.error("%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)",d))}h.isReactWarning=!0,Object.defineProperty(t,"key",{get:h,configurable:!0})}function o(){var t=e(this.type);return V[t]||(V[t]=!0,console.error("Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release.")),t=this.props.ref,t!==void 0?t:null}function u(t,d,h,y,J,Z){var j=h.ref;return t={$$typeof:F,type:t,key:d,props:h,_owner:y},(j!==void 0?j:null)!==null?Object.defineProperty(t,"ref",{enumerable:!1,get:o}):Object.defineProperty(t,"ref",{enumerable:!1,value:null}),t._store={},Object.defineProperty(t._store,"validated",{configurable:!1,enumerable:!1,writable:!0,value:0}),Object.defineProperty(t,"_debugInfo",{configurable:!1,enumerable:!1,writable:!0,value:null}),Object.defineProperty(t,"_debugStack",{configurable:!1,enumerable:!1,writable:!0,value:J}),Object.defineProperty(t,"_debugTask",{configurable:!1,enumerable:!1,writable:!0,value:Z}),Object.freeze&&(Object.freeze(t.props),Object.freeze(t)),t}function i(t,d,h,y,J,Z){var j=d.children;if(j!==void 0)if(y)if(v(j)){for(y=0;y<j.length;y++)T(j[y]);Object.freeze&&Object.freeze(j)}else console.error("React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead.");else T(j);if(k.call(d,"key")){j=e(t);var L=Object.keys(d).filter(function(we){return we!=="key"});y=0<L.length?"{key: someKey, "+L.join(": ..., ")+": ...}":"{key: someKey}",de[j+y]||(L=0<L.length?"{"+L.join(": ..., ")+": ...}":"{}",console.error(`A props object containing a "key" prop is being spread into JSX:
1
+ (function(E,D){typeof exports=="object"&&typeof module<"u"?D(exports,require("react"),require("@object-ui/core"),require("@object-ui/react"),require("lucide-react"),require("@object-ui/components")):typeof define=="function"&&define.amd?define(["exports","react","@object-ui/core","@object-ui/react","lucide-react","@object-ui/components"],D):(E=typeof globalThis<"u"?globalThis:E||self,D(E.ObjectUIPluginCalendar={},E.React,E.ObjectUICore,E.ObjectUIReact,E.LucideReact,E.ObjectUIComponents))})(this,(function(E,D,re,fe,$,g){"use strict";function me(e){const n=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(e){for(const l in e)if(l!=="default"){const u=Object.getOwnPropertyDescriptor(e,l);Object.defineProperty(n,l,u.get?u:{enumerable:!0,get:()=>e[l]})}}return n.default=e,Object.freeze(n)}const W=me(D);var U={exports:{}},M={};var ae;function ge(){if(ae)return M;ae=1;var e=Symbol.for("react.transitional.element"),n=Symbol.for("react.fragment");function l(u,f,s){var m=null;if(s!==void 0&&(m=""+s),f.key!==void 0&&(m=""+f.key),"key"in f){s={};for(var a in f)a!=="key"&&(s[a]=f[a])}else s=f;return f=s.ref,{$$typeof:e,type:u,key:m,ref:f!==void 0?f:null,props:s}}return M.Fragment=n,M.jsx=l,M.jsxs=l,M}var I={};var ne;function pe(){return ne||(ne=1,process.env.NODE_ENV!=="production"&&(function(){function e(t){if(t==null)return null;if(typeof t=="function")return t.$$typeof===C?null:t.displayName||t.name||null;if(typeof t=="string")return t;switch(t){case k:return"Fragment";case q:return"Profiler";case Y:return"StrictMode";case O:return"Suspense";case Q:return"SuspenseList";case _:return"Activity"}if(typeof t=="object")switch(typeof t.tag=="number"&&console.error("Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."),t.$$typeof){case L:return"Portal";case w:return t.displayName||"Context";case c:return(t._context.displayName||"Context")+".Consumer";case T:var o=t.render;return t=t.displayName,t||(t=o.displayName||o.name||"",t=t!==""?"ForwardRef("+t+")":"ForwardRef"),t;case Z:return o=t.displayName||null,o!==null?o:e(t.type)||"Memo";case b:o=t._payload,t=t._init;try{return e(t(o))}catch{}}return null}function n(t){return""+t}function l(t){try{n(t);var o=!1}catch{o=!0}if(o){o=console;var x=o.error,j=typeof Symbol=="function"&&Symbol.toStringTag&&t[Symbol.toStringTag]||t.constructor.name||"Object";return x.call(o,"The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",j),n(t)}}function u(t){if(t===k)return"<>";if(typeof t=="object"&&t!==null&&t.$$typeof===b)return"<...>";try{var o=e(t);return o?"<"+o+">":"<...>"}catch{return"<...>"}}function f(){var t=S.A;return t===null?null:t.getOwner()}function s(){return Error("react-stack-top-frame")}function m(t){if(h.call(t,"key")){var o=Object.getOwnPropertyDescriptor(t,"key").get;if(o&&o.isReactWarning)return!1}return t.key!==void 0}function a(t,o){function x(){R||(R=!0,console.error("%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)",o))}x.isReactWarning=!0,Object.defineProperty(t,"key",{get:x,configurable:!0})}function d(){var t=e(this.type);return z[t]||(z[t]=!0,console.error("Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release.")),t=this.props.ref,t!==void 0?t:null}function i(t,o,x,j,J,ee){var v=x.ref;return t={$$typeof:V,type:t,key:o,props:x,_owner:j},(v!==void 0?v:null)!==null?Object.defineProperty(t,"ref",{enumerable:!1,get:d}):Object.defineProperty(t,"ref",{enumerable:!1,value:null}),t._store={},Object.defineProperty(t._store,"validated",{configurable:!1,enumerable:!1,writable:!0,value:0}),Object.defineProperty(t,"_debugInfo",{configurable:!1,enumerable:!1,writable:!0,value:null}),Object.defineProperty(t,"_debugStack",{configurable:!1,enumerable:!1,writable:!0,value:J}),Object.defineProperty(t,"_debugTask",{configurable:!1,enumerable:!1,writable:!0,value:ee}),Object.freeze&&(Object.freeze(t.props),Object.freeze(t)),t}function N(t,o,x,j,J,ee){var v=o.children;if(v!==void 0)if(j)if(K(v)){for(j=0;j<v.length;j++)p(v[j]);Object.freeze&&Object.freeze(v)}else console.error("React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead.");else p(v);if(h.call(o,"key")){v=e(t);var P=Object.keys(o).filter(function(Se){return Se!=="key"});j=0<P.length?"{key: someKey, "+P.join(": ..., ")+": ...}":"{key: someKey}",ue[v+j]||(P=0<P.length?"{"+P.join(": ..., ")+": ...}":"{}",console.error(`A props object containing a "key" prop is being spread into JSX:
2
2
  let props = %s;
3
3
  <%s {...props} />
4
4
  React keys must be passed directly to JSX without using spread:
5
5
  let props = %s;
6
- <%s key={someKey} {...props} />`,y,j,L,j),de[j+y]=!0)}if(j=null,h!==void 0&&(n(h),j=""+h),m(d)&&(n(d.key),j=""+d.key),"key"in d){h={};for(var K in d)K!=="key"&&(h[K]=d[K])}else h=d;return j&&a(h,typeof t=="function"?t.displayName||t.name||"Unknown":t),u(t,j,h,g(),J,Z)}function T(t){S(t)?t._store&&(t._store.validated=1):typeof t=="object"&&t!==null&&t.$$typeof===W&&(t._payload.status==="fulfilled"?S(t._payload.value)&&t._payload.value._store&&(t._payload.value._store.validated=1):t._store&&(t._store.validated=1))}function S(t){return typeof t=="object"&&t!==null&&t.$$typeof===F}var w=p,F=Symbol.for("react.transitional.element"),A=Symbol.for("react.portal"),M=Symbol.for("react.fragment"),s=Symbol.for("react.strict_mode"),C=Symbol.for("react.profiler"),P=Symbol.for("react.consumer"),I=Symbol.for("react.context"),R=Symbol.for("react.forward_ref"),X=Symbol.for("react.suspense"),q=Symbol.for("react.suspense_list"),Q=Symbol.for("react.memo"),W=Symbol.for("react.lazy"),D=Symbol.for("react.activity"),b=Symbol.for("react.client.reference"),x=w.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,k=Object.prototype.hasOwnProperty,v=Array.isArray,E=console.createTask?console.createTask:function(){return null};w={react_stack_bottom_frame:function(t){return t()}};var O,V={},H=w.react_stack_bottom_frame.bind(w,c)(),z=E(f(c)),de={};$.Fragment=M,$.jsx=function(t,d,h){var y=1e4>x.recentlyCreatedOwnerStacks++;return i(t,d,h,!1,y?Error("react-stack-top-frame"):H,y?E(f(t)):z)},$.jsxs=function(t,d,h){var y=1e4>x.recentlyCreatedOwnerStacks++;return i(t,d,h,!0,y?Error("react-stack-top-frame"):H,y?E(f(t)):z)}})()),$}var le;function ge(){return le||(le=1,process.env.NODE_ENV==="production"?U.exports=fe():U.exports=me()),U.exports}var r=ge();function pe(e){return e.data?e.data:e.staticData?{provider:"value",items:e.staticData}:e.objectName?{provider:"object",object:e.objectName}:null}function be(e){if(e){if(typeof e=="string"){const l=e.split(" "),n=l[0],f=l[1]?.toLowerCase()==="desc"?"desc":"asc";return{[n]:f}}if(Array.isArray(e))return e.reduce((l,n)=>(n.field&&n.order&&(l[n.field]=n.order),l),{})}}function xe(e){return e.filter&&typeof e.filter=="object"&&"calendar"in e.filter?e.filter.calendar:e.calendar?e.calendar:null}const oe=({schema:e,dataSource:l,className:n,onEventClick:f,onDateClick:g})=>{const[c,m]=p.useState([]),[a,o]=p.useState(!0),[u,i]=p.useState(null),[T,S]=p.useState(null),[w,F]=p.useState(new Date),[A,M]=p.useState("month"),s=pe(e),C=xe(e),P=s?.provider==="value";p.useEffect(()=>{(async()=>{try{if(o(!0),P&&s?.provider==="value"){m(s.items),o(!1);return}if(!l)throw new Error("DataSource required for object/api providers");if(s?.provider==="object"){const b=s.object,x=await l.find(b,{$filter:e.filter,$orderby:be(e.sort)});m(x?.data||[])}else s?.provider==="api"&&(console.warn("API provider not yet implemented for ObjectCalendar"),m([]));o(!1)}catch(b){i(b),o(!1)}})()},[s,l,P,e.filter,e.sort]),p.useEffect(()=>{!P&&l&&(async()=>{try{if(!l)return;const b=s?.provider==="object"?s.object:e.objectName;if(!b)return;const x=await l.getObjectSchema(b);S(x)}catch(b){console.error("Failed to fetch object schema:",b)}})()},[e.objectName,l,P,s]);const I=p.useMemo(()=>{if(!C||!c.length)return[];const{startDateField:D,endDateField:b,titleField:x,colorField:k}=C;return c.map((v,E)=>{const O=v[D],V=b?v[b]:null,H=v[x]||"Untitled",z=k?v[k]:void 0;return{id:v.id||v._id||`event-${E}`,title:H,start:O?new Date(O):new Date,end:V?new Date(V):void 0,color:z,allDay:!V,data:v}}).filter(v=>!isNaN(v.start.getTime()))},[c,C]),R=p.useMemo(()=>{const D=w.getFullYear(),b=w.getMonth(),x=new Date(D,b,1),k=new Date(x);k.setDate(k.getDate()-k.getDay());const v=[],E=new Date(k);for(let O=0;O<42;O++)v.push(new Date(E)),E.setDate(E.getDate()+1);return v},[w]),X=p.useCallback(D=>I.filter(b=>{const x=new Date(b.start);x.setHours(0,0,0,0);const k=b.end?new Date(b.end):x;k.setHours(23,59,59,999);const v=new Date(D);return v.setHours(0,0,0,0),v>=x&&v<=k}),[I]),q=p.useCallback(D=>{F(b=>{const x=new Date(b);return x.setMonth(x.getMonth()+(D==="next"?1:-1)),x})},[]);if(a)return r.jsx("div",{className:n,children:r.jsx("div",{className:"flex items-center justify-center h-96",children:r.jsx("div",{className:"text-muted-foreground",children:"Loading calendar..."})})});if(u)return r.jsx("div",{className:n,children:r.jsx("div",{className:"flex items-center justify-center h-96",children:r.jsxs("div",{className:"text-destructive",children:["Error: ",u.message]})})});if(!C)return r.jsx("div",{className:n,children:r.jsx("div",{className:"flex items-center justify-center h-96",children:r.jsx("div",{className:"text-muted-foreground",children:"Calendar configuration required. Please specify startDateField and titleField."})})});const Q=["January","February","March","April","May","June","July","August","September","October","November","December"],W=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"];return r.jsx("div",{className:n,children:r.jsxs("div",{className:"border rounded-lg bg-background",children:[r.jsxs("div",{className:"flex items-center justify-between p-4 border-b",children:[r.jsxs("h2",{className:"text-xl font-semibold",children:[Q[w.getMonth()]," ",w.getFullYear()]}),r.jsxs("div",{className:"flex gap-2",children:[r.jsx("button",{onClick:()=>q("prev"),className:"px-3 py-1 border rounded hover:bg-muted",children:"Previous"}),r.jsx("button",{onClick:()=>F(new Date),className:"px-3 py-1 border rounded hover:bg-muted",children:"Today"}),r.jsx("button",{onClick:()=>q("next"),className:"px-3 py-1 border rounded hover:bg-muted",children:"Next"})]})]}),r.jsxs("div",{className:"p-4",children:[r.jsx("div",{className:"grid grid-cols-7 gap-px mb-px",children:W.map(D=>r.jsx("div",{className:"text-center text-sm font-medium text-muted-foreground py-2",children:D},D))}),r.jsx("div",{className:"grid grid-cols-7 gap-px bg-border",children:R.map((D,b)=>{const x=X(D),k=D.getMonth()===w.getMonth(),v=D.getDate()===new Date().getDate()&&D.getMonth()===new Date().getMonth()&&D.getFullYear()===new Date().getFullYear();return r.jsxs("div",{className:`min-h-24 bg-background p-2 ${k?"":"text-muted-foreground bg-muted/30"} ${v?"ring-2 ring-primary":""}`,onClick:()=>g?.(D),children:[r.jsx("div",{className:"text-sm font-medium mb-1",children:D.getDate()}),r.jsxs("div",{className:"space-y-1",children:[x.slice(0,3).map(E=>r.jsx("div",{className:"text-xs px-1 py-0.5 rounded bg-primary/10 hover:bg-primary/20 cursor-pointer truncate",onClick:O=>{O.stopPropagation(),f?.(E.data)},style:E.color?{borderLeft:`3px solid ${E.color}`}:void 0,children:E.title},E.id)),x.length>3&&r.jsxs("div",{className:"text-xs text-muted-foreground",children:["+",x.length-3," more"]})]})]},b)})})]})]})})},G="bg-blue-500 text-white";function ie({events:e=[],view:l="month",currentDate:n=new Date,onEventClick:f,onDateClick:g,onViewChange:c,onNavigate:m,className:a}){const[o,u]=re.useState(l),[i,T]=re.useState(n),S=()=>{const s=new Date(i);o==="month"?s.setMonth(s.getMonth()-1):o==="week"?s.setDate(s.getDate()-7):s.setDate(s.getDate()-1),T(s),m?.(s)},w=()=>{const s=new Date(i);o==="month"?s.setMonth(s.getMonth()+1):o==="week"?s.setDate(s.getDate()+7):s.setDate(s.getDate()+1),T(s),m?.(s)},F=()=>{const s=new Date;T(s),m?.(s)},A=s=>{u(s),c?.(s)},M=()=>{if(o==="month")return i.toLocaleDateString("default",{month:"long",year:"numeric"});if(o==="week"){const s=se(i),C=new Date(s);return C.setDate(C.getDate()+6),`${s.toLocaleDateString("default",{month:"short",day:"numeric"})} - ${C.toLocaleDateString("default",{month:"short",day:"numeric",year:"numeric"})}`}else return i.toLocaleDateString("default",{weekday:"long",month:"long",day:"numeric",year:"numeric"})};return r.jsxs("div",{className:N.cn("flex flex-col h-full bg-background",a),children:[r.jsxs("div",{className:"flex items-center justify-between p-4 border-b",children:[r.jsxs("div",{className:"flex items-center gap-2",children:[r.jsx(N.Button,{variant:"outline",size:"sm",onClick:F,children:"Today"}),r.jsxs("div",{className:"flex items-center",children:[r.jsx(N.Button,{variant:"ghost",size:"icon",onClick:S,className:"h-8 w-8",children:r.jsx(te.ChevronLeftIcon,{className:"h-4 w-4"})}),r.jsx(N.Button,{variant:"ghost",size:"icon",onClick:w,className:"h-8 w-8",children:r.jsx(te.ChevronRightIcon,{className:"h-4 w-4"})})]}),r.jsx("h2",{className:"text-lg font-semibold ml-2",children:M()})]}),r.jsx("div",{className:"flex items-center gap-2",children:r.jsxs(N.Select,{value:o,onValueChange:A,children:[r.jsx(N.SelectTrigger,{className:"w-32",children:r.jsx(N.SelectValue,{})}),r.jsxs(N.SelectContent,{children:[r.jsx(N.SelectItem,{value:"day",children:"Day"}),r.jsx(N.SelectItem,{value:"week",children:"Week"}),r.jsx(N.SelectItem,{value:"month",children:"Month"})]})]})})]}),r.jsxs("div",{className:"flex-1 overflow-auto",children:[o==="month"&&r.jsx(he,{date:i,events:e,onEventClick:f,onDateClick:g}),o==="week"&&r.jsx(ve,{date:i,events:e,onEventClick:f,onDateClick:g}),o==="day"&&r.jsx(ye,{date:i,events:e,onEventClick:f})]})]})}function se(e){const l=new Date(e),n=l.getDay(),f=l.getDate()-n;return l.setDate(f),l}function De(e){const l=e.getFullYear(),n=e.getMonth(),f=new Date(l,n,1),g=new Date(l,n+1,0),c=f.getDay(),m=[];for(let o=c-1;o>=0;o--){const u=new Date(f.getTime());u.setDate(u.getDate()-(o+1)),m.push(u)}for(let o=1;o<=g.getDate();o++)m.push(new Date(l,n,o));const a=42-m.length;for(let o=1;o<=a;o++){const u=new Date(g.getTime());u.setDate(u.getDate()+o),m.push(u)}return m}function ce(e,l){return e.getFullYear()===l.getFullYear()&&e.getMonth()===l.getMonth()&&e.getDate()===l.getDate()}function B(e,l){return l.filter(n=>{const f=new Date(n.start),g=n.end?new Date(n.end):new Date(f),c=new Date(e);c.setHours(0,0,0,0);const m=new Date(e);m.setHours(23,59,59,999);const a=new Date(f);a.setHours(0,0,0,0);const o=new Date(g);return o.setHours(23,59,59,999),c<=o&&m>=a})}function he({date:e,events:l,onEventClick:n,onDateClick:f}){const g=De(e),c=new Date,m=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"];return r.jsxs("div",{className:"flex flex-col h-full",children:[r.jsx("div",{className:"grid grid-cols-7 border-b",children:m.map(a=>r.jsx("div",{className:"p-2 text-center text-sm font-medium text-muted-foreground border-r last:border-r-0",children:a},a))}),r.jsx("div",{className:"grid grid-cols-7 flex-1 auto-rows-fr",children:g.map((a,o)=>{const u=B(a,l),i=a.getMonth()===e.getMonth(),T=ce(a,c);return r.jsxs("div",{className:N.cn("border-b border-r last:border-r-0 p-2 min-h-[100px] cursor-pointer hover:bg-accent/50",!i&&"bg-muted/30 text-muted-foreground"),onClick:()=>f?.(a),children:[r.jsx("div",{className:N.cn("text-sm font-medium mb-1",T&&"inline-flex items-center justify-center rounded-full bg-primary text-primary-foreground h-6 w-6"),children:a.getDate()}),r.jsxs("div",{className:"space-y-1",children:[u.slice(0,3).map(S=>r.jsx("div",{className:N.cn("text-xs px-2 py-1 rounded truncate cursor-pointer hover:opacity-80",S.color||G),style:S.color&&S.color.startsWith("#")?{backgroundColor:S.color}:void 0,onClick:w=>{w.stopPropagation(),n?.(S)},children:S.title},S.id)),u.length>3&&r.jsxs("div",{className:"text-xs text-muted-foreground px-2",children:["+",u.length-3," more"]})]})]},o)})})]})}function ve({date:e,events:l,onEventClick:n,onDateClick:f}){const g=se(e),c=Array.from({length:7},(a,o)=>{const u=new Date(g);return u.setDate(u.getDate()+o),u}),m=new Date;return r.jsxs("div",{className:"flex flex-col h-full",children:[r.jsx("div",{className:"grid grid-cols-7 border-b",children:c.map(a=>{const o=ce(a,m);return r.jsxs("div",{className:"p-3 text-center border-r last:border-r-0",children:[r.jsx("div",{className:"text-sm font-medium text-muted-foreground",children:a.toLocaleDateString("default",{weekday:"short"})}),r.jsx("div",{className:N.cn("text-lg font-semibold mt-1",o&&"inline-flex items-center justify-center rounded-full bg-primary text-primary-foreground h-8 w-8"),children:a.getDate()})]},a.toISOString())})}),r.jsx("div",{className:"grid grid-cols-7 flex-1",children:c.map(a=>{const o=B(a,l);return r.jsx("div",{className:"border-r last:border-r-0 p-2 min-h-[400px] cursor-pointer hover:bg-accent/50",onClick:()=>f?.(a),children:r.jsx("div",{className:"space-y-2",children:o.map(u=>r.jsxs("div",{className:N.cn("text-sm px-3 py-2 rounded cursor-pointer hover:opacity-80",u.color||G),style:u.color&&u.color.startsWith("#")?{backgroundColor:u.color}:void 0,onClick:i=>{i.stopPropagation(),n?.(u)},children:[r.jsx("div",{className:"font-medium",children:u.title}),!u.allDay&&r.jsx("div",{className:"text-xs opacity-90 mt-1",children:u.start.toLocaleTimeString("default",{hour:"numeric",minute:"2-digit"})})]},u.id))})},a.toISOString())})})]})}function ye({date:e,events:l,onEventClick:n}){const f=B(e,l),g=Array.from({length:24},(c,m)=>m);return r.jsx("div",{className:"flex flex-col h-full",children:r.jsx("div",{className:"flex-1 overflow-auto",children:g.map(c=>{const m=f.filter(a=>a.allDay?c===0:a.start.getHours()===c);return r.jsxs("div",{className:"flex border-b min-h-[60px]",children:[r.jsx("div",{className:"w-20 p-2 text-sm text-muted-foreground border-r",children:c===0?"12 AM":c<12?`${c} AM`:c===12?"12 PM":`${c-12} PM`}),r.jsx("div",{className:"flex-1 p-2 space-y-2",children:m.map(a=>r.jsxs("div",{className:N.cn("px-3 py-2 rounded cursor-pointer hover:opacity-80",a.color||G),style:a.color&&a.color.startsWith("#")?{backgroundColor:a.color}:void 0,onClick:()=>n?.(a),children:[r.jsx("div",{className:"font-medium",children:a.title}),!a.allDay&&r.jsxs("div",{className:"text-xs opacity-90 mt-1",children:[a.start.toLocaleTimeString("default",{hour:"numeric",minute:"2-digit"}),a.end&&` - ${a.end.toLocaleTimeString("default",{hour:"numeric",minute:"2-digit"})}`]})]},a.id))})]},c)})})})}ee.ComponentRegistry.register("calendar-view",({schema:e,className:l,onAction:n,...f})=>{const g=p.useMemo(()=>!e.data||!Array.isArray(e.data)?[]:e.data.map((i,T)=>{const S=e.titleField||"title",w=e.startDateField||"start",F=e.endDateField||"end",A=e.colorField||"color",M=e.allDayField||"allDay",s=i[S]||"Untitled",C=i[w]?new Date(i[w]):new Date,P=i[F]?new Date(i[F]):void 0,I=i[M]!==void 0?i[M]:!1;let R=i[A];return R&&e.colorMapping&&e.colorMapping[R]&&(R=e.colorMapping[R]),{id:String(i.id||i._id||T),title:s,start:C,end:P,allDay:I,color:R,data:i}}),[e.data,e.titleField,e.startDateField,e.endDateField,e.colorField,e.allDayField,e.colorMapping]),c=p.useCallback(i=>{n&&n({type:"event_click",payload:{event:i.data,eventId:i.id}}),e.onEventClick&&e.onEventClick(i.data)},[n,e]),m=p.useCallback(i=>{n&&n({type:"date_click",payload:{date:i}}),e.onDateClick&&e.onDateClick(i)},[n,e]),a=p.useCallback(i=>{n&&n({type:"view_change",payload:{view:i}}),e.onViewChange&&e.onViewChange(i)},[n,e]),o=p.useCallback(i=>{n&&n({type:"navigate",payload:{date:i}}),e.onNavigate&&e.onNavigate(i)},[n,e]),u=e.view&&["month","week","day"].includes(e.view)?e.view:"month";return r.jsx(ie,{events:g,view:u,currentDate:e.currentDate?new Date(e.currentDate):void 0,onEventClick:c,onDateClick:e.allowCreate||e.onDateClick?m:void 0,onViewChange:a,onNavigate:o,className:l,...f})},{label:"Calendar View",inputs:[{name:"data",type:"array",label:"Data",description:"Array of record objects to display as events"},{name:"titleField",type:"string",label:"Title Field",defaultValue:"title",description:"Field name to use for event title"},{name:"startDateField",type:"string",label:"Start Date Field",defaultValue:"start",description:"Field name for event start date"},{name:"endDateField",type:"string",label:"End Date Field",defaultValue:"end",description:"Field name for event end date (optional)"},{name:"allDayField",type:"string",label:"All Day Field",defaultValue:"allDay",description:"Field name for all-day flag"},{name:"colorField",type:"string",label:"Color Field",defaultValue:"color",description:"Field name for event color"},{name:"colorMapping",type:"object",label:"Color Mapping",description:'Map field values to colors (e.g., {meeting: "blue", deadline: "red"})'},{name:"view",type:"enum",enum:["month","week","day"],defaultValue:"month",label:"View Mode",description:"Calendar view mode (month, week, or day)"},{name:"currentDate",type:"string",label:"Current Date",description:"ISO date string for initial calendar date"},{name:"allowCreate",type:"boolean",label:"Allow Create",defaultValue:!1,description:"Allow creating events by clicking on dates"},{name:"className",type:"string",label:"CSS Class"}],defaultProps:{view:"month",titleField:"title",startDateField:"start",endDateField:"end",allDayField:"allDay",colorField:"color",allowCreate:!1,data:[{id:1,title:"Team Meeting",start:new Date(new Date().setHours(10,0,0,0)).toISOString(),end:new Date(new Date().setHours(11,0,0,0)).toISOString(),color:"#3b82f6",allDay:!1},{id:2,title:"Project Deadline",start:new Date(new Date().setDate(new Date().getDate()+3)).toISOString(),color:"#ef4444",allDay:!0},{id:3,title:"Conference",start:new Date(new Date().setDate(new Date().getDate()+7)).toISOString(),end:new Date(new Date().setDate(new Date().getDate()+9)).toISOString(),color:"#10b981",allDay:!0}],className:"h-[600px] border rounded-lg"}});const je=({schema:e})=>r.jsx(oe,{schema:e,dataSource:null});ee.ComponentRegistry.register("object-calendar",je,{label:"Object Calendar",category:"plugin",inputs:[{name:"objectName",type:"string",label:"Object Name",required:!0},{name:"calendar",type:"object",label:"Calendar Config",description:"startDateField, endDateField, titleField, colorField"}]}),_.CalendarView=ie,_.ObjectCalendar=oe,Object.defineProperty(_,Symbol.toStringTag,{value:"Module"})}));
6
+ <%s key={someKey} {...props} />`,j,v,P,v),ue[v+j]=!0)}if(v=null,x!==void 0&&(l(x),v=""+x),m(o)&&(l(o.key),v=""+o.key),"key"in o){x={};for(var te in o)te!=="key"&&(x[te]=o[te])}else x=o;return v&&a(x,typeof t=="function"?t.displayName||t.name||"Unknown":t),i(t,v,x,f(),J,ee)}function p(t){y(t)?t._store&&(t._store.validated=1):typeof t=="object"&&t!==null&&t.$$typeof===b&&(t._payload.status==="fulfilled"?y(t._payload.value)&&t._payload.value._store&&(t._payload.value._store.validated=1):t._store&&(t._store.validated=1))}function y(t){return typeof t=="object"&&t!==null&&t.$$typeof===V}var F=D,V=Symbol.for("react.transitional.element"),L=Symbol.for("react.portal"),k=Symbol.for("react.fragment"),Y=Symbol.for("react.strict_mode"),q=Symbol.for("react.profiler"),c=Symbol.for("react.consumer"),w=Symbol.for("react.context"),T=Symbol.for("react.forward_ref"),O=Symbol.for("react.suspense"),Q=Symbol.for("react.suspense_list"),Z=Symbol.for("react.memo"),b=Symbol.for("react.lazy"),_=Symbol.for("react.activity"),C=Symbol.for("react.client.reference"),S=F.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,h=Object.prototype.hasOwnProperty,K=Array.isArray,A=console.createTask?console.createTask:function(){return null};F={react_stack_bottom_frame:function(t){return t()}};var R,z={},H=F.react_stack_bottom_frame.bind(F,s)(),de=A(u(s)),ue={};I.Fragment=k,I.jsx=function(t,o,x){var j=1e4>S.recentlyCreatedOwnerStacks++;return N(t,o,x,!1,j?Error("react-stack-top-frame"):H,j?A(u(t)):de)},I.jsxs=function(t,o,x){var j=1e4>S.recentlyCreatedOwnerStacks++;return N(t,o,x,!0,j?Error("react-stack-top-frame"):H,j?A(u(t)):de)}})()),I}var le;function be(){return le||(le=1,process.env.NODE_ENV==="production"?U.exports=ge():U.exports=pe()),U.exports}var r=be();const B="bg-blue-500 text-white";function G({events:e=[],view:n="month",currentDate:l=new Date,onEventClick:u,onDateClick:f,onViewChange:s,onNavigate:m,onAddClick:a,className:d}){const[i,N]=W.useState(n),[p,y]=W.useState(l);W.useEffect(()=>{y(l)},[l]),W.useEffect(()=>{N(n)},[n]);const F=()=>{const c=new Date(p);i==="month"?c.setMonth(c.getMonth()-1):i==="week"?c.setDate(c.getDate()-7):c.setDate(c.getDate()-1),y(c),m?.(c)},V=()=>{const c=new Date(p);i==="month"?c.setMonth(c.getMonth()+1):i==="week"?c.setDate(c.getDate()+7):c.setDate(c.getDate()+1),y(c),m?.(c)},L=()=>{const c=new Date;y(c),m?.(c)},k=c=>{N(c),s?.(c)},Y=()=>{if(i==="month")return p.toLocaleDateString("default",{month:"long",year:"numeric"});if(i==="week"){const c=ie(p),w=new Date(c);return w.setDate(w.getDate()+6),`${c.toLocaleDateString("default",{month:"short",day:"numeric"})} - ${w.toLocaleDateString("default",{month:"short",day:"numeric",year:"numeric"})}`}else return p.toLocaleDateString("default",{weekday:"long",month:"long",day:"numeric",year:"numeric"})},q=c=>{c&&(y(c),m?.(c))};return r.jsxs("div",{className:g.cn("flex flex-col h-full bg-background",d),children:[r.jsxs("div",{className:"flex items-center justify-between p-4 border-b",children:[r.jsxs("div",{className:"flex items-center gap-4",children:[r.jsxs("div",{className:"flex items-center bg-muted/50 rounded-lg p-1 gap-1",children:[r.jsx(g.Button,{variant:"ghost",size:"sm",onClick:L,className:"h-8",children:"Today"}),r.jsx("div",{className:"h-4 w-px bg-border mx-1"}),r.jsx(g.Button,{variant:"ghost",size:"icon",onClick:F,className:"h-8 w-8",children:r.jsx($.ChevronLeftIcon,{className:"h-4 w-4"})}),r.jsx(g.Button,{variant:"ghost",size:"icon",onClick:V,className:"h-8 w-8",children:r.jsx($.ChevronRightIcon,{className:"h-4 w-4"})})]}),r.jsxs(g.Popover,{children:[r.jsx(g.PopoverTrigger,{asChild:!0,children:r.jsxs(g.Button,{variant:"ghost",className:g.cn("text-xl font-semibold h-auto px-3 py-1 hover:bg-muted/50 transition-colors","flex items-center gap-2"),children:[r.jsx($.CalendarIcon,{className:"h-5 w-5 text-muted-foreground"}),r.jsx("span",{children:Y()})]})}),r.jsx(g.PopoverContent,{className:"w-auto p-0",align:"start",children:r.jsx(g.Calendar,{mode:"single",selected:p,onSelect:q,initialFocus:!0,fromYear:2e3,toYear:2050})})]})]}),r.jsxs("div",{className:"flex items-center gap-2",children:[r.jsxs(g.Select,{value:i,onValueChange:k,children:[r.jsx(g.SelectTrigger,{className:"w-32 bg-background",children:r.jsx(g.SelectValue,{})}),r.jsxs(g.SelectContent,{children:[r.jsx(g.SelectItem,{value:"day",children:"Day"}),r.jsx(g.SelectItem,{value:"week",children:"Week"}),r.jsx(g.SelectItem,{value:"month",children:"Month"})]})]}),a&&r.jsxs(g.Button,{onClick:a,size:"sm",className:"gap-1",children:[r.jsx($.PlusIcon,{className:"h-4 w-4"}),"New"]})]})]}),r.jsxs("div",{className:"flex-1 overflow-auto",children:[i==="month"&&r.jsx(ye,{date:p,events:e,onEventClick:u,onDateClick:f}),i==="week"&&r.jsx(De,{date:p,events:e,onEventClick:u,onDateClick:f}),i==="day"&&r.jsx(je,{date:p,events:e,onEventClick:u})]})]})}function ie(e){const n=new Date(e),l=n.getDay(),u=n.getDate()-l;return n.setDate(u),n}function xe(e){const n=e.getFullYear(),l=e.getMonth(),u=new Date(n,l,1),f=new Date(n,l+1,0),s=u.getDay(),m=[];for(let d=s-1;d>=0;d--){const i=new Date(u.getTime());i.setDate(i.getDate()-(d+1)),m.push(i)}for(let d=1;d<=f.getDate();d++)m.push(new Date(n,l,d));const a=42-m.length;for(let d=1;d<=a;d++){const i=new Date(f.getTime());i.setDate(i.getDate()+d),m.push(i)}return m}function oe(e,n){return e.getFullYear()===n.getFullYear()&&e.getMonth()===n.getMonth()&&e.getDate()===n.getDate()}function X(e,n){return n.filter(l=>{const u=new Date(l.start),f=l.end?new Date(l.end):new Date(u),s=new Date(e);s.setHours(0,0,0,0);const m=new Date(e);m.setHours(23,59,59,999);const a=new Date(u);a.setHours(0,0,0,0);const d=new Date(f);return d.setHours(23,59,59,999),s<=d&&m>=a})}function ye({date:e,events:n,onEventClick:l,onDateClick:u}){const f=xe(e),s=new Date,m=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"];return r.jsxs("div",{className:"flex flex-col h-full",children:[r.jsx("div",{className:"grid grid-cols-7 border-b",children:m.map(a=>r.jsx("div",{className:"p-2 text-center text-sm font-medium text-muted-foreground border-r last:border-r-0",children:a},a))}),r.jsx("div",{className:"grid grid-cols-7 flex-1 auto-rows-fr",children:f.map((a,d)=>{const i=X(a,n),N=a.getMonth()===e.getMonth(),p=oe(a,s);return r.jsxs("div",{className:g.cn("border-b border-r last:border-r-0 p-2 min-h-[100px] cursor-pointer hover:bg-accent/50",!N&&"bg-muted/30 text-muted-foreground"),onClick:()=>u?.(a),children:[r.jsx("div",{className:g.cn("text-sm font-medium mb-1",p&&"inline-flex items-center justify-center rounded-full bg-primary text-primary-foreground h-6 w-6"),children:a.getDate()}),r.jsxs("div",{className:"space-y-1",children:[i.slice(0,3).map(y=>r.jsx("div",{className:g.cn("text-xs px-2 py-1 rounded truncate cursor-pointer hover:opacity-80",y.color||B),style:y.color&&y.color.startsWith("#")?{backgroundColor:y.color}:void 0,onClick:F=>{F.stopPropagation(),l?.(y)},children:y.title},y.id)),i.length>3&&r.jsxs("div",{className:"text-xs text-muted-foreground px-2",children:["+",i.length-3," more"]})]})]},d)})})]})}function De({date:e,events:n,onEventClick:l,onDateClick:u}){const f=ie(e),s=Array.from({length:7},(a,d)=>{const i=new Date(f);return i.setDate(i.getDate()+d),i}),m=new Date;return r.jsxs("div",{className:"flex flex-col h-full",children:[r.jsx("div",{className:"grid grid-cols-7 border-b",children:s.map(a=>{const d=oe(a,m);return r.jsxs("div",{className:"p-3 text-center border-r last:border-r-0",children:[r.jsx("div",{className:"text-sm font-medium text-muted-foreground",children:a.toLocaleDateString("default",{weekday:"short"})}),r.jsx("div",{className:g.cn("text-lg font-semibold mt-1",d&&"inline-flex items-center justify-center rounded-full bg-primary text-primary-foreground h-8 w-8"),children:a.getDate()})]},a.toISOString())})}),r.jsx("div",{className:"grid grid-cols-7 flex-1",children:s.map(a=>{const d=X(a,n);return r.jsx("div",{className:"border-r last:border-r-0 p-2 min-h-[400px] cursor-pointer hover:bg-accent/50",onClick:()=>u?.(a),children:r.jsx("div",{className:"space-y-2",children:d.map(i=>r.jsxs("div",{className:g.cn("text-sm px-3 py-2 rounded cursor-pointer hover:opacity-80",i.color||B),style:i.color&&i.color.startsWith("#")?{backgroundColor:i.color}:void 0,onClick:N=>{N.stopPropagation(),l?.(i)},children:[r.jsx("div",{className:"font-medium",children:i.title}),!i.allDay&&r.jsx("div",{className:"text-xs opacity-90 mt-1",children:i.start.toLocaleTimeString("default",{hour:"numeric",minute:"2-digit"})})]},i.id))})},a.toISOString())})})]})}function je({date:e,events:n,onEventClick:l}){const u=X(e,n),f=Array.from({length:24},(s,m)=>m);return r.jsx("div",{className:"flex flex-col h-full",children:r.jsx("div",{className:"flex-1 overflow-auto",children:f.map(s=>{const m=u.filter(a=>a.allDay?s===0:a.start.getHours()===s);return r.jsxs("div",{className:"flex border-b min-h-[60px]",children:[r.jsx("div",{className:"w-20 p-2 text-sm text-muted-foreground border-r",children:s===0?"12 AM":s<12?`${s} AM`:s===12?"12 PM":`${s-12} PM`}),r.jsx("div",{className:"flex-1 p-2 space-y-2",children:m.map(a=>r.jsxs("div",{className:g.cn("px-3 py-2 rounded cursor-pointer hover:opacity-80",a.color||B),style:a.color&&a.color.startsWith("#")?{backgroundColor:a.color}:void 0,onClick:()=>l?.(a),children:[r.jsx("div",{className:"font-medium",children:a.title}),!a.allDay&&r.jsxs("div",{className:"text-xs opacity-90 mt-1",children:[a.start.toLocaleTimeString("default",{hour:"numeric",minute:"2-digit"}),a.end&&` - ${a.end.toLocaleTimeString("default",{hour:"numeric",minute:"2-digit"})}`]})]},a.id))})]},s)})})})}function ve(e){return"data"in e&&e.data?e.data:"staticData"in e&&e.staticData?{provider:"value",items:e.staticData}:e.objectName?{provider:"object",object:e.objectName}:null}function he(e){if(e){if(typeof e=="string"){const n=e.split(" "),l=n[0],u=n[1]?.toLowerCase()==="desc"?"desc":"asc";return{[l]:u}}if(Array.isArray(e))return e.reduce((n,l)=>(l.field&&l.order&&(n[l.field]=l.order),n),{})}}function we(e){return"filter"in e&&e.filter&&typeof e.filter=="object"&&"calendar"in e.filter?e.filter.calendar:e.calendar?e.calendar:e.startDateField||e.dateField?{startDateField:e.startDateField||e.dateField,endDateField:e.endDateField||e.endField,titleField:e.titleField||"name",colorField:e.colorField,allDayField:e.allDayField}:null}const se=({schema:e,dataSource:n,className:l,onEventClick:u,onDateClick:f,onNavigate:s,onViewChange:m,...a})=>{const[d,i]=D.useState([]),[N,p]=D.useState(!0),[y,F]=D.useState(null),[V,L]=D.useState(null),[k,Y]=D.useState(new Date),[q,c]=D.useState("month"),w=D.useMemo(()=>ve(e),[e.data,e.staticData,e.objectName]),T=D.useMemo(()=>we(e),[e.filter,e.calendar,e.dateField,e.endField,e.titleField,e.colorField]),O=w?.provider==="value";D.useEffect(()=>{let b=!0;return(async()=>{try{if(!b)return;if(p(!0),O&&w?.provider==="value"){b&&(i(w.items),p(!1));return}if(e.data||a.data){const C=e.data||a.data;if(Array.isArray(C)){i(C),p(!1);return}}if(!n)throw new Error("DataSource required for object/api providers");if(w?.provider==="object"){const C=w.object,S=await n.find(C,{$filter:e.filter,$orderby:he(e.sort)});let h=[];Array.isArray(S)?h=S:S&&typeof S=="object"&&(Array.isArray(S.data)?h=S.data:Array.isArray(S.value)&&(h=S.value)),b&&i(h)}else w?.provider==="api"&&(console.warn("API provider not yet implemented for ObjectCalendar"),b&&i([]));b&&p(!1)}catch(C){console.error("[ObjectCalendar] Error fetching data:",C),b&&(F(C),p(!1))}})(),()=>{b=!1}},[w,n,O,e.filter,e.sort]),D.useEffect(()=>{!O&&n&&(async()=>{try{if(!n)return;const _=w?.provider==="object"?w.object:e.objectName;if(!_)return;const C=await n.getObjectSchema(_);L(C)}catch(_){console.error("Failed to fetch object schema:",_)}})()},[e.objectName,n,O,w]);const Q=D.useMemo(()=>{if(!T||!d.length)return[];const{startDateField:b,endDateField:_,titleField:C,colorField:S}=T;return d.map((h,K)=>{const A=h[b],R=_?h[_]:null,z=h[C]||"Untitled",H=S?h[S]:void 0;return{id:h.id||h._id||`event-${K}`,title:z,start:A?new Date(A):new Date,end:R?new Date(R):void 0,color:H,allDay:!R,data:h}}).filter(h=>!isNaN(h.start.getTime()))},[d,T]),Z=D.useCallback(()=>{f?.(new Date)},[f]);return N?r.jsx("div",{className:l,children:r.jsx("div",{className:"flex items-center justify-center h-96",children:r.jsx("div",{className:"text-muted-foreground",children:"Loading calendar..."})})}):y?r.jsx("div",{className:l,children:r.jsx("div",{className:"flex items-center justify-center h-96",children:r.jsxs("div",{className:"text-destructive",children:["Error: ",y.message]})})}):T?r.jsx("div",{className:l,children:r.jsx("div",{className:"border rounded-lg bg-background h-[calc(100vh-200px)] min-h-[600px]",children:r.jsx(G,{events:Q,currentDate:k,view:e.defaultView||"month",onEventClick:b=>u?.(b.data),onDateClick:f,onNavigate:b=>{Y(b),s?.(b)},onViewChange:b=>{c(b),m?.(b)},onAddClick:Z})})}):r.jsx("div",{className:l,children:r.jsx("div",{className:"flex items-center justify-center h-96",children:r.jsx("div",{className:"text-muted-foreground",children:"Calendar configuration required. Please specify startDateField and titleField."})})})};re.ComponentRegistry.register("calendar-view",({schema:e,className:n,onAction:l,...u})=>{const f=D.useMemo(()=>!e.data||!Array.isArray(e.data)?[]:e.data.map((a,d)=>{const i=e.titleField||"title",N=e.startDateField||"start",p=e.endDateField||"end",y=e.colorField||"color",F=e.allDayField||"allDay";return{id:a._id||a.id||d,title:a[i]||"Untitled Event",start:new Date(a[N]),end:a[p]?new Date(a[p]):void 0,allDay:a[F],color:a[y],data:a}}),[e.data,e.titleField,e.startDateField,e.endDateField,e.colorField,e.allDayField]),s=a=>{e.onEventClick&&l?.({type:"event-click",payload:a})},m=()=>{l?.({type:"create",payload:{}})};return r.jsx(G,{className:n,events:f,onEventClick:s,onAddClick:m,...u})},{namespace:"plugin-calendar",label:"Calendar View",inputs:[{name:"data",type:"array",label:"Data",description:"Array of record objects to display as events"},{name:"titleField",type:"string",label:"Title Field",defaultValue:"title",description:"Field name to use for event title"},{name:"startDateField",type:"string",label:"Start Date Field",defaultValue:"start",description:"Field name for event start date"},{name:"endDateField",type:"string",label:"End Date Field",defaultValue:"end",description:"Field name for event end date (optional)"},{name:"allDayField",type:"string",label:"All Day Field",defaultValue:"allDay",description:"Field name for all-day flag"},{name:"colorField",type:"string",label:"Color Field",defaultValue:"color",description:"Field name for event color"},{name:"colorMapping",type:"object",label:"Color Mapping",description:'Map field values to colors (e.g., {meeting: "blue", deadline: "red"})'},{name:"view",type:"enum",enum:["month","week","day"],defaultValue:"month",label:"View Mode",description:"Calendar view mode (month, week, or day)"},{name:"currentDate",type:"string",label:"Current Date",description:"ISO date string for initial calendar date"},{name:"allowCreate",type:"boolean",label:"Allow Create",defaultValue:!1,description:"Allow creating events by clicking on dates"},{name:"className",type:"string",label:"CSS Class"}],defaultProps:{view:"month",titleField:"title",startDateField:"start",endDateField:"end",allDayField:"allDay",colorField:"color",allowCreate:!1,data:[{id:1,title:"Team Meeting",start:new Date(new Date().setHours(10,0,0,0)).toISOString(),end:new Date(new Date().setHours(11,0,0,0)).toISOString(),color:"#3b82f6",allDay:!1},{id:2,title:"Project Deadline",start:new Date(new Date().setDate(new Date().getDate()+3)).toISOString(),color:"#ef4444",allDay:!0},{id:3,title:"Conference",start:new Date(new Date().setDate(new Date().getDate()+7)).toISOString(),end:new Date(new Date().setDate(new Date().getDate()+9)).toISOString(),color:"#10b981",allDay:!0}],className:"h-[600px] border rounded-lg"}});const ce=({schema:e})=>{const{dataSource:n}=fe.useSchemaContext();return r.jsx(se,{schema:e,dataSource:n})};re.ComponentRegistry.register("object-calendar",ce,{namespace:"plugin-calendar",label:"Object Calendar",category:"plugin",inputs:[{name:"objectName",type:"string",label:"Object Name",required:!0},{name:"calendar",type:"object",label:"Calendar Config",description:"startDateField, endDateField, titleField, colorField"}]}),E.CalendarView=G,E.ObjectCalendar=se,E.ObjectCalendarRenderer=ce,Object.defineProperty(E,Symbol.toStringTag,{value:"Module"})}));
@@ -22,8 +22,9 @@ export interface CalendarViewProps {
22
22
  onDateClick?: (date: Date) => void;
23
23
  onViewChange?: (view: "month" | "week" | "day") => void;
24
24
  onNavigate?: (date: Date) => void;
25
+ onAddClick?: () => void;
25
26
  className?: string;
26
27
  }
27
- declare function CalendarView({ events, view, currentDate, onEventClick, onDateClick, onViewChange, onNavigate, className, }: CalendarViewProps): import("react/jsx-runtime").JSX.Element;
28
+ declare function CalendarView({ events, view, currentDate, onEventClick, onDateClick, onViewChange, onNavigate, onAddClick, className, }: CalendarViewProps): import("react/jsx-runtime").JSX.Element;
28
29
  export { CalendarView };
29
30
  //# sourceMappingURL=CalendarView.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"CalendarView.d.ts","sourceRoot":"","sources":["../../src/CalendarView.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAUH,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,GAAG,MAAM,CAAA;IACnB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,IAAI,CAAA;IACX,GAAG,CAAC,EAAE,IAAI,CAAA;IACV,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,GAAG,CAAA;CACX;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,EAAE,aAAa,EAAE,CAAA;IACxB,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,KAAK,CAAA;IAC/B,WAAW,CAAC,EAAE,IAAI,CAAA;IAClB,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAA;IAC7C,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAA;IAClC,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,KAAK,KAAK,IAAI,CAAA;IACvD,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAA;IACjC,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,iBAAS,YAAY,CAAC,EACpB,MAAW,EACX,IAAc,EACd,WAAwB,EACxB,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,UAAU,EACV,SAAS,GACV,EAAE,iBAAiB,2CA2InB;AA8TD,OAAO,EAAE,YAAY,EAAE,CAAA"}
1
+ {"version":3,"file":"CalendarView.d.ts","sourceRoot":"","sources":["../../src/CalendarView.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAsBH,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,GAAG,MAAM,CAAA;IACnB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,IAAI,CAAA;IACX,GAAG,CAAC,EAAE,IAAI,CAAA;IACV,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,GAAG,CAAA;CACX;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,EAAE,aAAa,EAAE,CAAA;IACxB,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,KAAK,CAAA;IAC/B,WAAW,CAAC,EAAE,IAAI,CAAA;IAClB,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAA;IAC7C,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAA;IAClC,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,KAAK,KAAK,IAAI,CAAA;IACvD,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAA;IACjC,UAAU,CAAC,EAAE,MAAM,IAAI,CAAA;IACvB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,iBAAS,YAAY,CAAC,EACpB,MAAW,EACX,IAAc,EACd,WAAwB,EACxB,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,UAAU,EACV,UAAU,EACV,SAAS,GACV,EAAE,iBAAiB,2CA4LnB;AA8TD,OAAO,EAAE,YAAY,EAAE,CAAA"}
@@ -1,13 +1,27 @@
1
1
  import { default as React } from 'react';
2
2
  import { ObjectGridSchema, DataSource } from '@object-ui/types';
3
+ export interface CalendarSchema {
4
+ type: 'calendar';
5
+ objectName?: string;
6
+ dateField?: string;
7
+ endField?: string;
8
+ titleField?: string;
9
+ colorField?: string;
10
+ filter?: any;
11
+ sort?: any;
12
+ /** Initial view mode */
13
+ defaultView?: 'month' | 'week' | 'day';
14
+ }
3
15
  export interface ObjectCalendarProps {
4
- schema: ObjectGridSchema;
16
+ schema: ObjectGridSchema | CalendarSchema;
5
17
  dataSource?: DataSource;
6
18
  className?: string;
7
19
  onEventClick?: (record: any) => void;
8
20
  onDateClick?: (date: Date) => void;
9
21
  onEdit?: (record: any) => void;
10
22
  onDelete?: (record: any) => void;
23
+ onNavigate?: (date: Date) => void;
24
+ onViewChange?: (view: 'month' | 'week' | 'day') => void;
11
25
  }
12
26
  export declare const ObjectCalendar: React.FC<ObjectCalendarProps>;
13
27
  //# sourceMappingURL=ObjectCalendar.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ObjectCalendar.d.ts","sourceRoot":"","sources":["../../src/ObjectCalendar.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAoD,MAAM,OAAO,CAAC;AACzE,OAAO,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAA4B,MAAM,kBAAkB,CAAC;AAE/F,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,gBAAgB,CAAC;IACzB,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,IAAI,CAAC;IACrC,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IACnC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,IAAI,CAAC;IAC/B,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,IAAI,CAAC;CAClC;AAuED,eAAO,MAAM,cAAc,EAAE,KAAK,CAAC,EAAE,CAAC,mBAAmB,CAsRxD,CAAC"}
1
+ {"version":3,"file":"ObjectCalendar.d.ts","sourceRoot":"","sources":["../../src/ObjectCalendar.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAoD,MAAM,OAAO,CAAC;AACzE,OAAO,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAA4B,MAAM,kBAAkB,CAAC;AAG/F,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,UAAU,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,GAAG,CAAC;IACb,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,wBAAwB;IACxB,WAAW,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,KAAK,CAAC;CACxC;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,gBAAgB,GAAG,cAAc,CAAC;IAC1C,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,IAAI,CAAC;IACrC,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IACnC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,IAAI,CAAC;IAC/B,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,IAAI,CAAC;IACjC,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IAClC,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,KAAK,KAAK,IAAI,CAAC;CACzD;AAkFD,eAAO,MAAM,cAAc,EAAE,KAAK,CAAC,EAAE,CAAC,mBAAmB,CAuNxD,CAAC"}
@@ -1,6 +1,10 @@
1
+ import { default as React } from 'react';
1
2
  import { ObjectCalendar, ObjectCalendarProps } from './ObjectCalendar';
2
3
  export { ObjectCalendar };
3
4
  export type { ObjectCalendarProps };
4
5
  export { CalendarView } from './CalendarView';
5
6
  export type { CalendarViewProps, CalendarEvent } from './CalendarView';
7
+ export declare const ObjectCalendarRenderer: React.FC<{
8
+ schema: any;
9
+ }>;
6
10
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAG5D,OAAO,EAAE,cAAc,EAAE,CAAC;AAC1B,YAAY,EAAE,mBAAmB,EAAE,CAAC;AAGpC,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,YAAY,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAGvE,OAAO,0BAA0B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAG5D,OAAO,EAAE,cAAc,EAAE,CAAC;AAC1B,YAAY,EAAE,mBAAmB,EAAE,CAAC;AAGpC,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,YAAY,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAGvE,OAAO,0BAA0B,CAAC;AAGlC,eAAO,MAAM,sBAAsB,EAAE,KAAK,CAAC,EAAE,CAAC;IAAE,MAAM,EAAE,GAAG,CAAA;CAAE,CAG5D,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@object-ui/plugin-calendar",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "Calendar view plugins for Object UI - includes both ObjectQL-integrated and standalone calendar components",
@@ -25,23 +25,24 @@
25
25
  },
26
26
  "dependencies": {
27
27
  "lucide-react": "^0.563.0",
28
- "@object-ui/components": "0.3.0",
29
- "@object-ui/core": "0.3.0",
30
- "@object-ui/fields": "0.3.0",
31
- "@object-ui/react": "0.3.0",
32
- "@object-ui/types": "0.3.0"
28
+ "@object-ui/components": "0.5.0",
29
+ "@object-ui/core": "0.5.0",
30
+ "@object-ui/fields": "0.5.0",
31
+ "@object-ui/react": "0.5.0",
32
+ "@object-ui/types": "0.5.0"
33
33
  },
34
34
  "peerDependencies": {
35
35
  "react": "^18.0.0 || ^19.0.0",
36
36
  "react-dom": "^18.0.0 || ^19.0.0"
37
37
  },
38
38
  "devDependencies": {
39
- "@types/react": "^19.2.9",
39
+ "@types/react": "^19.2.10",
40
40
  "@types/react-dom": "^19.2.3",
41
- "@vitejs/plugin-react": "^4.2.1",
41
+ "@vitejs/plugin-react": "^5.1.3",
42
42
  "typescript": "^5.9.3",
43
43
  "vite": "^7.3.1",
44
- "vite-plugin-dts": "^4.5.4"
44
+ "vite-plugin-dts": "^4.5.4",
45
+ "@object-ui/data-objectstack": "0.5.0"
45
46
  },
46
47
  "scripts": {
47
48
  "build": "vite build",
@@ -0,0 +1,118 @@
1
+ import { describe, it, expect, vi } from 'vitest';
2
+ import { render, screen, fireEvent } from '@testing-library/react';
3
+ import { CalendarView, CalendarEvent } from './CalendarView';
4
+ import React from 'react';
5
+
6
+ // Mock ResizeObserver
7
+ class ResizeObserver {
8
+ observe() {}
9
+ unobserve() {}
10
+ disconnect() {}
11
+ }
12
+ global.ResizeObserver = ResizeObserver;
13
+
14
+ // Mock PointerEvents which are not in JSDOM but needed for Radix
15
+ if (!global.PointerEvent) {
16
+ class PointerEvent extends Event {
17
+ button: number;
18
+ ctrlKey: boolean;
19
+ metaKey: boolean;
20
+ shiftKey: boolean;
21
+ constructor(type: string, props: any = {}) {
22
+ super(type, props);
23
+ this.button = props.button || 0;
24
+ this.ctrlKey = props.ctrlKey || false;
25
+ this.metaKey = props.metaKey || false;
26
+ this.shiftKey = props.shiftKey || false;
27
+ }
28
+ }
29
+ // @ts-expect-error Mocking global PointerEvent
30
+ global.PointerEvent = PointerEvent as any;
31
+ }
32
+
33
+ // Mock HTMLElement.offsetParent for Radix Popper
34
+ Object.defineProperty(HTMLElement.prototype, 'offsetParent', {
35
+ get() {
36
+ return this.parentElement;
37
+ },
38
+ });
39
+
40
+ describe('CalendarView', () => {
41
+ const mockEvents: CalendarEvent[] = [
42
+ {
43
+ id: '1',
44
+ title: 'Test Event 1',
45
+ start: new Date(2024, 0, 15, 10, 0), // Jan 15, 2024
46
+ end: new Date(2024, 0, 15, 11, 0),
47
+ }
48
+ ];
49
+
50
+ const defaultDate = new Date(2024, 0, 15); // Jan 15, 2024
51
+
52
+ it('renders the header correctly', () => {
53
+ render(<CalendarView currentDate={defaultDate} />);
54
+
55
+ // Check for month label
56
+ expect(screen.getByText('January 2024')).toBeInTheDocument();
57
+ });
58
+
59
+ it('renders navigation buttons', () => {
60
+ render(<CalendarView currentDate={defaultDate} />);
61
+
62
+ expect(screen.getByText('Today')).toBeInTheDocument();
63
+
64
+ // We expect 5 buttons:
65
+ // 1. Today
66
+ // 2. Prev (Chevron)
67
+ // 3. Next (Chevron)
68
+ // 4. Date Picker Trigger (Button wrapping text)
69
+ // 5. Select View Trigger (Button)
70
+
71
+ const buttons = screen.getAllByRole('button');
72
+ // Just verify we have the essential ones
73
+ expect(buttons.length).toBeGreaterThanOrEqual(4);
74
+
75
+ // Verify specific triggers via aria-expanded or combobox functionality if possible,
76
+ // or just by existence to satisfy "I don't see the dropdown" check.
77
+ });
78
+
79
+ it('renders the view switcher dropdown trigger', () => {
80
+ render(<CalendarView currentDate={defaultDate} view="month" />);
81
+ // The SelectValue should display "Month"
82
+ const selectTrigger = screen.getByText('Month');
83
+ expect(selectTrigger).toBeInTheDocument();
84
+
85
+ // Ensure it's inside a button (Radix Select Trigger)
86
+ const triggerButton = selectTrigger.closest('button');
87
+ expect(triggerButton).toBeInTheDocument();
88
+ });
89
+
90
+ it('renders the date picker trigger', () => {
91
+ render(<CalendarView currentDate={defaultDate} />);
92
+ // The date label (e.g. "January 2024") is now inside a PopoverTrigger button
93
+ const dateLabel = screen.getByText('January 2024');
94
+ expect(dateLabel).toBeInTheDocument();
95
+
96
+ const triggerButton = dateLabel.closest('button');
97
+ expect(triggerButton).toBeInTheDocument();
98
+ expect(triggerButton).toHaveClass('text-xl font-semibold');
99
+ });
100
+
101
+ it('opens date picker on click', () => {
102
+ // We need to mock pointer interactions for Popover usually, but let's try basic click
103
+ render(<CalendarView currentDate={defaultDate} />);
104
+ const dateTrigger = screen.getByText('January 2024');
105
+
106
+ fireEvent.click(dateTrigger);
107
+
108
+ // After click, the Calendar component inside PopoverContent should appear.
109
+ // However, Radix portals might make this tricky in simple jest-dom without proper setup.
110
+ // We just verify the trigger is clickable.
111
+ expect(dateTrigger).toBeEnabled();
112
+ });
113
+
114
+ it('renders events in month view', () => {
115
+ render(<CalendarView currentDate={defaultDate} events={mockEvents} />);
116
+ expect(screen.getByText('Test Event 1')).toBeInTheDocument();
117
+ });
118
+ });
@@ -9,8 +9,20 @@
9
9
  "use client"
10
10
 
11
11
  import * as React from "react"
12
- import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react"
13
- import { cn, Button, Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@object-ui/components"
12
+ import { ChevronLeftIcon, ChevronRightIcon, CalendarIcon, PlusIcon } from "lucide-react"
13
+ import {
14
+ cn,
15
+ Button,
16
+ Select,
17
+ SelectContent,
18
+ SelectItem,
19
+ SelectTrigger,
20
+ SelectValue,
21
+ Calendar,
22
+ Popover,
23
+ PopoverContent,
24
+ PopoverTrigger
25
+ } from "@object-ui/components"
14
26
 
15
27
  const DEFAULT_EVENT_COLOR = "bg-blue-500 text-white"
16
28
 
@@ -32,6 +44,7 @@ export interface CalendarViewProps {
32
44
  onDateClick?: (date: Date) => void
33
45
  onViewChange?: (view: "month" | "week" | "day") => void
34
46
  onNavigate?: (date: Date) => void
47
+ onAddClick?: () => void
35
48
  className?: string
36
49
  }
37
50
 
@@ -43,11 +56,21 @@ function CalendarView({
43
56
  onDateClick,
44
57
  onViewChange,
45
58
  onNavigate,
59
+ onAddClick,
46
60
  className,
47
61
  }: CalendarViewProps) {
48
62
  const [selectedView, setSelectedView] = React.useState(view)
49
63
  const [selectedDate, setSelectedDate] = React.useState(currentDate)
50
64
 
65
+ // Sync state if props change
66
+ React.useEffect(() => {
67
+ setSelectedDate(currentDate)
68
+ }, [currentDate])
69
+
70
+ React.useEffect(() => {
71
+ setSelectedView(view)
72
+ }, [view])
73
+
51
74
  const handlePrevious = () => {
52
75
  const newDate = new Date(selectedDate)
53
76
  if (selectedView === "month") {
@@ -113,37 +136,70 @@ function CalendarView({
113
136
  }
114
137
  }
115
138
 
139
+ const handleDateSelect = (date: Date | undefined) => {
140
+ if (date) {
141
+ setSelectedDate(date)
142
+ onNavigate?.(date)
143
+ }
144
+ }
145
+
116
146
  return (
117
147
  <div className={cn("flex flex-col h-full bg-background", className)}>
118
148
  {/* Header */}
119
149
  <div className="flex items-center justify-between p-4 border-b">
120
- <div className="flex items-center gap-2">
121
- <Button variant="outline" size="sm" onClick={handleToday}>
122
- Today
123
- </Button>
124
- <div className="flex items-center">
125
- <Button
126
- variant="ghost"
127
- size="icon"
128
- onClick={handlePrevious}
129
- className="h-8 w-8"
130
- >
131
- <ChevronLeftIcon className="h-4 w-4" />
132
- </Button>
133
- <Button
134
- variant="ghost"
135
- size="icon"
136
- onClick={handleNext}
137
- className="h-8 w-8"
138
- >
139
- <ChevronRightIcon className="h-4 w-4" />
140
- </Button>
150
+ <div className="flex items-center gap-4">
151
+ <div className="flex items-center bg-muted/50 rounded-lg p-1 gap-1">
152
+ <Button variant="ghost" size="sm" onClick={handleToday} className="h-8">
153
+ Today
154
+ </Button>
155
+ <div className="h-4 w-px bg-border mx-1" />
156
+ <Button
157
+ variant="ghost"
158
+ size="icon"
159
+ onClick={handlePrevious}
160
+ className="h-8 w-8"
161
+ >
162
+ <ChevronLeftIcon className="h-4 w-4" />
163
+ </Button>
164
+ <Button
165
+ variant="ghost"
166
+ size="icon"
167
+ onClick={handleNext}
168
+ className="h-8 w-8"
169
+ >
170
+ <ChevronRightIcon className="h-4 w-4" />
171
+ </Button>
141
172
  </div>
142
- <h2 className="text-lg font-semibold ml-2">{getDateLabel()}</h2>
173
+
174
+ <Popover>
175
+ <PopoverTrigger asChild>
176
+ <Button
177
+ variant="ghost"
178
+ className={cn(
179
+ "text-xl font-semibold h-auto px-3 py-1 hover:bg-muted/50 transition-colors",
180
+ "flex items-center gap-2"
181
+ )}
182
+ >
183
+ <CalendarIcon className="h-5 w-5 text-muted-foreground" />
184
+ <span>{getDateLabel()}</span>
185
+ </Button>
186
+ </PopoverTrigger>
187
+ <PopoverContent className="w-auto p-0" align="start">
188
+ <Calendar
189
+ mode="single"
190
+ selected={selectedDate}
191
+ onSelect={handleDateSelect}
192
+ initialFocus
193
+ fromYear={2000}
194
+ toYear={2050}
195
+ />
196
+ </PopoverContent>
197
+ </Popover>
143
198
  </div>
199
+
144
200
  <div className="flex items-center gap-2">
145
201
  <Select value={selectedView} onValueChange={handleViewChange}>
146
- <SelectTrigger className="w-32">
202
+ <SelectTrigger className="w-32 bg-background">
147
203
  <SelectValue />
148
204
  </SelectTrigger>
149
205
  <SelectContent>
@@ -152,6 +208,13 @@ function CalendarView({
152
208
  <SelectItem value="month">Month</SelectItem>
153
209
  </SelectContent>
154
210
  </Select>
211
+
212
+ {onAddClick && (
213
+ <Button onClick={onAddClick} size="sm" className="gap-1">
214
+ <PlusIcon className="h-4 w-4" />
215
+ New
216
+ </Button>
217
+ )}
155
218
  </div>
156
219
  </div>
157
220
 
@@ -0,0 +1,96 @@
1
+ import { describe, it, expect, vi, beforeAll, afterAll, afterEach } from 'vitest';
2
+ import { render, screen, waitFor } from '@testing-library/react';
3
+ import '@testing-library/jest-dom';
4
+ import { ObjectCalendar } from './ObjectCalendar';
5
+ import { ObjectStackAdapter } from '@object-ui/data-objectstack';
6
+ import { setupServer } from 'msw/node';
7
+ import { http, HttpResponse } from 'msw';
8
+ import React from 'react';
9
+
10
+ const BASE_URL = 'http://localhost';
11
+
12
+ // --- Mock Data ---
13
+
14
+ const mockEvents = {
15
+ value: [
16
+ {
17
+ _id: '1',
18
+ title: 'Meeting with Client',
19
+ start: new Date().toISOString(),
20
+ end: new Date(Date.now() + 3600000).toISOString(),
21
+ type: 'business'
22
+ },
23
+ {
24
+ _id: '2',
25
+ title: 'Team Lunch',
26
+ start: new Date(Date.now() + 86400000).toISOString(), // Tomorrow
27
+ type: 'personal'
28
+ }
29
+ ]
30
+ };
31
+
32
+ // --- MSW Setup ---
33
+
34
+ const handlers = [
35
+ http.options('*', () => {
36
+ return new HttpResponse(null, { status: 200 });
37
+ }),
38
+
39
+ http.get(`${BASE_URL}/api/v1`, () => {
40
+ return HttpResponse.json({ status: 'ok', version: '1.0.0' });
41
+ }),
42
+
43
+ // Data Query: GET /api/v1/data/events
44
+ http.get(`${BASE_URL}/api/v1/data/events`, () => {
45
+ return HttpResponse.json(mockEvents);
46
+ }),
47
+
48
+ // Metadata Query
49
+ http.get(`${BASE_URL}/api/v1/meta/object/events`, () => {
50
+ return HttpResponse.json({ fields: {} });
51
+ })
52
+ ];
53
+
54
+ const server = setupServer(...handlers);
55
+
56
+ // --- Test Suite ---
57
+
58
+ describe('ObjectCalendar with MSW', () => {
59
+ beforeAll(() => server.listen());
60
+ afterEach(() => server.resetHandlers());
61
+ afterAll(() => server.close());
62
+
63
+ it('fetches events and renders them', async () => {
64
+ const dataSource = new ObjectStackAdapter({
65
+ baseUrl: BASE_URL,
66
+ });
67
+
68
+ render(
69
+ <ObjectCalendar
70
+ schema={{
71
+ type: 'calendar',
72
+ objectName: 'events',
73
+ // Calendar specific config usually goes into 'calendar' prop or implicit mapping
74
+ calendar: {
75
+ dateField: 'start',
76
+ endDateField: 'end',
77
+ titleField: 'title'
78
+ }
79
+ }}
80
+ dataSource={dataSource}
81
+ />
82
+ );
83
+
84
+ // Verify events appear
85
+ await waitFor(() => {
86
+ expect(screen.getByText('Meeting with Client')).toBeInTheDocument();
87
+ });
88
+
89
+ // Check subsequent events (might need navigation depending on view,
90
+ // but default month view usually shows current month dates)
91
+ // Note: If today is near end of month, tomorrow might be in next month view.
92
+ // However, the test event is "now", so it should be visible.
93
+
94
+ // We can just assert the first event for now.
95
+ });
96
+ });