@qratilabs/qrati-connect 2.24.2 → 2.25.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.
package/README.md CHANGED
@@ -1,174 +1,130 @@
1
- <p align="center">
2
- <img src="https://qrati.com/images/qrati-connect-banner.jpg" alt="Qrati Connect Banner" style="max-width:100%;height:auto;" />
3
- </p>
1
+ ![Qrati Connect Architecture](https://assets.qrati.com/images/tech-docs/qrati-connect-architecture-29-06-2026.webp)
4
2
 
5
- <p align="center">
6
- <a href="https://www.npmjs.com/package/@qratilabs/qrati-connect"><img src="https://img.shields.io/npm/v/@qratilabs/qrati-connect.svg" alt="npm version"></a>
7
- <a href="https://www.npmjs.com/package/@qratilabs/qrati-connect"><img src="https://img.shields.io/npm/dm/@qratilabs/qrati-connect.svg" alt="npm downloads"></a>
8
- <a href="https://github.com/qrati-labs/qrati-connect-ts/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/@qratilabs/qrati-connect.svg" alt="license"></a>
9
- <img src="https://img.shields.io/badge/TypeScript-Ready-blue.svg" alt="TypeScript">
10
- </p>
3
+ ![Node.js](https://img.shields.io/badge/Node-24.12-339933?logo=node.js)
4
+ ![pnpm](https://img.shields.io/badge/pnpm-11-F69220?logo=pnpm)
5
+ ![Preact](https://img.shields.io/badge/Preact-10.29-673AB8?logo=preact)
6
+ ![Vite](https://img.shields.io/badge/Vite-8-646CFF?logo=vite)
7
+ ![TypeScript](https://img.shields.io/badge/TypeScript-6.0+-3178C6?logo=typescript)
8
+ ![Tailwind CSS](https://img.shields.io/badge/Tailwind%20CSS-v4-06B6D4?logo=tailwindcss)
9
+ ![Vitest](https://img.shields.io/badge/Tests-Vitest-6E9F18?logo=vitest)
10
+ ![Playwright](https://img.shields.io/badge/E2E-Playwright-2EAD33?logo=playwright)
11
+ ![Storybook](https://img.shields.io/badge/Storybook-10-FF4785?logo=storybook)
12
+ ![semantic-release](https://img.shields.io/badge/Release-semantic--release-CB3837?logo=semantic-release)
11
13
 
12
- ---
13
-
14
- # Qrati Connect — Embeddable Media & Engagement SDK
15
-
16
- **Qrati Connect** is a compact, production-grade SDK for embedding photo galleries, media uploads, curation, and engagement into any website. It is built with [Preact](https://preactjs.com/), strict TypeScript, and Shadow-DOM style isolation, then distributed as a Web Component, React wrapper, and zero-code embed script.
17
-
18
- ---
19
-
20
- ## Why Qrati Connect?
14
+ Qrati Connect is a drop-in, embeddable event/gallery widget. It ships as three friendly bundles:
21
15
 
22
- - **Framework-agnostic** — works in React, Vue, Angular, WordPress, or plain HTML.
23
- - **Style isolation** — Shadow DOM keeps SDK styles separate from the host page.
24
- - **Small initial payload** — Preact core plus strategic lazy-loading for heavy features (upload, HEIC conversion, maps).
25
- - **Themeable** — organization colors, fonts, radius, and shadows are fetched from the Qrati API and applied via CSS custom properties.
26
- - **TypeScript-first** — full typings for the public API.
16
+ - **`<qrati-connect>` Web Component** — shadow-DOM isolated, Preact-powered, ~3 KB.
17
+ - **React wrapper** — external React, lazy-loads the custom element.
18
+ - **Embed loader** — one `<script>` tag, no bundler needed.
27
19
 
28
- ---
29
-
30
- ## Highlights
31
-
32
- - Interactive media gallery with lightbox, blurhash placeholders, infinite scroll, and facial search.
33
- - Smart uploads: compression, chunked progress, video trimming, and HEIC → JPEG conversion on demand.
34
- - Reactions, curation workflow, leaderboards, and points-based engagement.
35
- - Public events page with folder navigation and optional map view (Google Maps, Mapbox, OpenStreetMap).
20
+ Styles self-inject at runtime, so hosts never import a stylesheet.
36
21
 
37
- ---
38
-
39
- ## Distribution Formats
40
22
 
41
- | Format | Output | Best for |
42
- |--------|--------|----------|
43
- | **React wrapper** | `dist/react/index.mjs` / `index.cjs` | React / Next.js apps |
44
- | **Web Component** | `dist/element/web.es.js` | Any framework or vanilla JS |
45
- | **Embed loader** | `dist/embed/embed.js` | Zero-code `<script>` integration |
23
+ ## Install
46
24
 
47
- The React wrapper and embed loader dynamically load the element build at runtime, so the same `web.es.js` bundle is always used.
25
+ ```bash
26
+ # contributors
27
+ pnpm install
48
28
 
49
- ---
29
+ # consumers
30
+ npm install @qratilabs/qrati-connect
31
+ ```
50
32
 
51
- ## Quick Install & Integration
33
+ > Requires **Node ≥24** and **pnpm ≥11**. See `.nvmrc` for the pinned Node version.
52
34
 
53
- ### React (client component)
35
+ ## Environment
54
36
 
55
37
  ```bash
56
- pnpm add @qratilabs/qrati-connect
57
- # or
58
- npm install @qratilabs/qrati-connect
38
+ cp .env.example .env.local
59
39
  ```
60
40
 
61
- ```tsx
62
- 'use client';
41
+ Set at least:
63
42
 
64
- import { QratiConnect } from '@qratilabs/qrati-connect';
43
+ - `VITE_BASE_URL` API base, e.g. `https://api.qrati.com`
44
+ - `VITE_APP_ENVIRONMENT` — e.g. `alpha`, `beta`, `prod`
65
45
 
66
- export default function Page() {
67
- return <QratiConnect organizationId='your-org-id' router='memory' />;
68
- }
69
- ```
46
+ ## Develop
70
47
 
71
- ### Web Component (framework-agnostic)
48
+ No separate dev server, but you have options:
72
49
 
73
- ```html
74
- <script type="module" src="https://cdn.jsdelivr.net/npm/@qratilabs/qrati-connect@2/element/web.es.js"></script>
50
+ ```bash
51
+ # Component workbench
52
+ pnpm storybook
75
53
 
76
- <qrati-connect organization-id="your-org-id" router="hash"></qrati-connect>
54
+ # Iterate on the standalone element
55
+ pnpm build:element:watch
56
+ pnpm serve:element # http://localhost:4599
77
57
  ```
78
58
 
79
- ### Embed script (zero-code)
59
+ ## Test
80
60
 
81
- ```html
82
- <script async
83
- src="https://cdn.jsdelivr.net/npm/@qratilabs/qrati-connect@2/embed/embed.js"
84
- data-organization-id="your-org-id"
85
- data-router="hash"></script>
61
+ ```bash
62
+ pnpm test:unit:run # Vitest + jsdom
63
+ pnpm test:production # Playwright against a live env
64
+ pnpm test:all # unit + production
86
65
  ```
87
66
 
88
- The embed script inserts a `<qrati-connect>` element next to the script tag (or into the element matched by `data-target`) and lazy-loads the element build.
67
+ ## Build
89
68
 
90
- ---
91
-
92
- ## Props & Attributes
93
-
94
- | Prop / Attribute | Type | Default | Description |
95
- |------------------|------|---------|-------------|
96
- | `organizationId` * | `string` | — | Qrati organization ID |
97
- | `router` | `'memory' \| 'hash'` | `'memory'` | Routing strategy |
98
- | `uid` | `string` | — | Pre-authenticated user ID |
99
- | `fname` | `string` | — | Pre-authenticated first name |
100
- | `lname` | `string` | — | Pre-authenticated last name |
101
- | `theme` | `'light' \| 'dark'` | `'light'` | Color scheme |
102
- | `onError` | `(error: Error) => void` | — | SDK error callback |
69
+ ```bash
70
+ pnpm build # all three bundles + post-build validation
71
+ pnpm build:element # dist/element/web.es.js
72
+ pnpm build:react # dist/react/index.{mjs,cjs,d.ts}
73
+ pnpm build:embed # dist/embed/embed.js
74
+ ```
103
75
 
104
- \* Required.
76
+ `build:react` automatically builds the element first because the React wrapper loads `../element/web.es.js` at runtime.
105
77
 
106
- Custom auth requires `uid` and `fname`; `lname` is optional.
78
+ ## Use it
107
79
 
108
- ---
80
+ ### React
109
81
 
110
- ## Events & Callbacks
82
+ ```tsx
83
+ import { QratiConnect } from '@qratilabs/qrati-connect';
111
84
 
112
- | Callback / Event | Payload | Description |
113
- |------------------|---------|-------------|
114
- | `onError` | `Error` | Called when an SDK error is caught |
115
- | `qrati-error` (custom event) | `{ error: Error }` | Dispatched on `window` and the custom element |
85
+ <QratiConnect
86
+ organizationId="your-org-id"
87
+ router="hash"
88
+ theme="light"
89
+ onError={(err) => console.error(err)}
90
+ />
91
+ ```
116
92
 
117
- React wrapper users should use the `onError` prop. Vanilla Web Component users can listen for `qrati-error`.
93
+ ### Script tag
118
94
 
119
- ---
95
+ ```html
96
+ <script
97
+ async
98
+ src="https://cdn.jsdelivr.net/npm/@qratilabs/qrati-connect/embed/embed.js"
99
+ data-organization-id="your-org-id"
100
+ data-router="hash"
101
+ data-theme="light"
102
+ ></script>
103
+ ```
120
104
 
121
- ## Performance & Bundling
105
+ Use a **classic** `<script>` tag — not `type="module"` — so the loader can read `document.currentScript`.
122
106
 
123
- Sizes are approximate and depend on the build output:
107
+ ### Web Component (programmatic)
124
108
 
125
- - React wrapper: ~1–2 KB gzipped (loads element build at runtime).
126
- - Initial element bundle: ~139 KB gzipped.
127
- - Upload feature: loaded on demand.
128
- - HEIC conversion (`heic2any`): loaded only when a `.heic` file is selected.
129
- - Maps and heavy UI libraries are split into lazy chunks.
109
+ ```ts
110
+ import { renderComponent } from '@qratilabs/qrati-connect/element';
130
111
 
131
- ---
132
-
133
- ## Development
112
+ renderComponent('#qrati-host', {
113
+ organizationId: 'your-org-id',
114
+ router: 'memory',
115
+ theme: 'dark'
116
+ });
117
+ ```
134
118
 
135
- This repository uses **pnpm** and **Node.js 24.x**.
119
+ ## Deploy
136
120
 
137
121
  ```bash
138
- pnpm install # install dependencies
139
- pnpm storybook # start Storybook on http://localhost:6006
140
- pnpm type-check # TypeScript check (no emit)
141
- pnpm lint # ESLint
142
- pnpm format # Prettier
143
- pnpm build # build all distribution targets
144
- pnpm test:unit # Vitest unit tests (watch)
145
- pnpm test:unit:run # Vitest unit tests (once)
146
- pnpm test:all # unit tests + Playwright production regression
122
+ pnpm deploy:beta # merge main into release/beta and push
123
+ pnpm deploy:prod # merge main into release/prod and push
147
124
  ```
148
125
 
149
- See [`docs/DEVELOPMENT.md`](docs/DEVELOPMENT.md) for the full setup guide and [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) for design details.
150
-
151
- ---
152
-
153
- ## Documentation
154
-
155
- - [`docs/INDEX.md`](docs/INDEX.md) — documentation map
156
- - [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) — architecture, build system, routing, styling, i18n
157
- - [`docs/DEVELOPMENT.md`](docs/DEVELOPMENT.md) — environment setup, workflow, testing, Storybook
158
- - [`docs/API.md`](docs/API.md) — backend API communication
159
- - [`docs/REFERENCE.md`](docs/REFERENCE.md) — components, hooks, pages, utilities, router API
160
- - [`docs/DEPLOYMENT.md`](docs/DEPLOYMENT.md) — release process and CDN
161
- - [`docs/FAQ.md`](docs/FAQ.md) — common questions
126
+ Both scripts require a clean git working tree. After pushing, semantic-release publishes from `dist/` when CI runs on the release branch.
162
127
 
163
128
  ---
164
129
 
165
- ## Support
166
-
167
- - GitHub Issues: https://github.com/qrati-labs/qrati-connect-ts/issues
168
- - Email: support@qrati.com
169
-
170
- ---
171
-
172
- ## License
173
-
174
- Proprietary © Qrati Labs — https://qrati.com. All rights reserved.
130
+ Built with Preact, Vite, Tailwind, and a little bit of Shadow DOM magic.
@@ -1,14 +1,14 @@
1
- import{_ as e,f as t,h as a,p as n,t as r,w as o}from"./DrrpB4Om.js";import{L as s,N as i,O as l,i as c,n as d,p as m,u}from"./CaX2xgrP.js";import{n as p,r as h,t as g}from"./C12iSy3L.js";import{n as f,r as v,t as x}from"./dT6WSX65.js";import{c as N,l as y}from"./ChJXhA9c.js";import{t as w}from"./CATr-3VW.js";import{t as b}from"./DF0LxcV7.js";import{t as T}from"./DGbyGc8b.js";import{t as D}from"./C3dOFk9A.js";import{n as P,r as C,t as j}from"./BOqkzZ92.js";import{t as I}from"./DnQ6XaLR.js";import{n as E,o as S,r as z,t as A}from"./DZtaQWVG2.js";import{t as U}from"./N4HKgEC52.js";import{a as q,i as _,n as M,r as O,t as k}from"./lcKuIs2T2.js";var $=({content:e,onDeleted:t,onClose:a})=>{const n=function(){const e=s(),{user:t,props:a}=d(),n=a?.organizationId,r=t?.token;return v({mutationKey:["delete-upload",r??null],mutationFn:async({contentId:e})=>{const t=await i.post(u.DELETE_CONTENT,{json:{contentId:e},headers:{"x-org-id":n,...r?{Authorization:`Bearer ${r}`}:{}}}).json();if(t?.error)throw new Error(t.error);return{contentId:e}},onMutate:async({contentId:t})=>{await e.cancelQueries({queryKey:[l.USER_UPLOADS],exact:!1});const a=e.getQueriesData({queryKey:[l.USER_UPLOADS],exact:!1});return e.setQueriesData({queryKey:[l.USER_UPLOADS],exact:!1},e=>e?.pages?{...e,pages:e.pages.map(e=>({...e,data:e.data.filter(e=>e._id!==t),meta:{...e.meta,totalCount:Math.max(0,(e.meta.totalCount??0)-1)}}))}:e),{previousData:a}},onError:(t,a,n)=>{n?.previousData?.forEach(([t,a])=>{e.setQueryData(t,a)})}})}(),o=e?.rateCount??0,c=o<=0,m=c?null:`This photo has ${o} rating${o>1?"s":""}. Photos with at least 1 rating cannot be deleted.`;/* @__PURE__ */
1
+ import{_ as e,f as t,h as a,p as n,t as r,w as o}from"./DrrpB4Om.js";import{L as s,N as i,O as l,i as c,n as d,p as m,u}from"./CaX2xgrP.js";import{n as p,r as h,t as g}from"./C12iSy3L.js";import{n as f,r as v,t as x}from"./dT6WSX65.js";import{c as N,l as y}from"./ChJXhA9c.js";import{t as w}from"./CATr-3VW.js";import{t as b}from"./DF0LxcV7.js";import{t as T}from"./DGbyGc8b.js";import{n as D,r as P,t as C}from"./EjMoIoSK.js";import{t as j}from"./DnQ6XaLR.js";import{t as I}from"./C3dOFk9A.js";import{t as E}from"./DPg_DKOi.js";import{n as S,o as z,r as A,t as U}from"./DZtaQWVG2.js";import{t as q}from"./N4HKgEC52.js";import{i as _,n as k,r as O,t as M}from"./Ctl9NdWl2.js";var $=({content:e,onDeleted:t,onClose:a})=>{const n=function(){const e=s(),{user:t,props:a}=d(),n=a?.organizationId,r=t?.token;return v({mutationKey:["delete-upload",r??null],mutationFn:async({contentId:e})=>{const t=await i.post(u.DELETE_CONTENT,{json:{contentId:e},headers:{"x-org-id":n,...r?{Authorization:`Bearer ${r}`}:{}}}).json();if(t?.error)throw new Error(t.error);return{contentId:e}},onMutate:async({contentId:t})=>{await e.cancelQueries({queryKey:[l.USER_UPLOADS],exact:!1});const a=e.getQueriesData({queryKey:[l.USER_UPLOADS],exact:!1});return e.setQueriesData({queryKey:[l.USER_UPLOADS],exact:!1},e=>e?.pages?{...e,pages:e.pages.map(e=>({...e,data:e.data.filter(e=>e._id!==t),meta:{...e.meta,totalCount:Math.max(0,(e.meta.totalCount??0)-1)}}))}:e),{previousData:a}},onError:(t,a,n)=>{n?.previousData?.forEach(([t,a])=>{e.setQueryData(t,a)})}})}(),o=e?.rateCount??0,c=o<=0,m=c?null:`This photo has ${o} rating${o>1?"s":""}. Photos with at least 1 rating cannot be deleted.`;/* @__PURE__ */
2
2
  return r(O,{open:Boolean(e),onOpenChange:e=>{e||a()},title:"Delete content?",description:m??"Are you sure you want to delete this content? This operation is permanent and cannot be undone.",cancelText:"Cancel",confirmText:c?"Delete":"Got it",confirmColor:c?"error":"primary",onCancel:a,onConfirm:c?()=>{if(!e||!c)return;const a=e.eventId,r=e._id;t(),n.mutate({contentId:r,eventId:a})}:a})};function L({className:e}){/* @__PURE__ */
3
- return r("svg",{xmlns:"http://www.w3.org/2000/svg",width:"1em",height:"1em",viewBox:"0 0 24 24",className:e,"aria-hidden":"true",focusable:"false",children:/* @__PURE__ */r("path",{fill:"currentColor",d:"M7 21q-.825 0-1.412-.587T5 19V6q-.425 0-.712-.288T4 5t.288-.712T5 4h4q0-.425.288-.712T10 3h4q.425 0 .713.288T15 4h4q.425 0 .713.288T20 5t-.288.713T19 6v13q0 .825-.587 1.413T17 21zM17 6H7v13h10zm-7 11q.425 0 .713-.288T11 16V9q0-.425-.288-.712T10 8t-.712.288T9 9v7q0 .425.288.713T10 17m4 0q.425 0 .713-.288T15 16V9q0-.425-.288-.712T14 8t-.712.288T13 9v7q0 .425.288.713T14 17M7 6v13z"})})}var R={[m.APPROVED]:{color:"text-success",bgColor:"bg-success/20",icon:/* @__PURE__ */r(k,{className:"size-full"}),title:"Approved",description:"Your upload has been approved and is now visible to everyone viewing this event."},[m.IN_REVIEW]:{color:"text-warning",bgColor:"bg-warning/20",icon:/* @__PURE__ */r(_,{className:"size-full"}),title:"In Review",description:"Your upload is currently being reviewed. It will be visible once approved by the event admin."},[m.REJECTED]:{color:"text-destructive",bgColor:"bg-destructive/20",icon:/* @__PURE__ */r(U,{className:"size-full"}),title:"Rejected",description:"Your upload was not approved as it may not meet the content guidelines. Please upload appropriate content."}},F=e=>{const t=new Date(e).getTime();if(Number.isNaN(t))return"";const a=Math.max(0,Math.floor((Date.now()-t)/1e3)),n=[[60,"second"],[60,"minute"],[24,"hour"],[30,"day"],[12,"month"],[Number.POSITIVE_INFINITY,"year"]];let r=a,o="second";for(const[s,i]of n){if(r<s){o=i;break}r=Math.floor(r/s),o=i}return"second"===o&&r<30?"just now":`${r} ${o}${1===r?"":"s"} ago`},V=({content:e,onDelete:t,onStatusClick:a})=>{const n=e.status,o=R[n];/* @__PURE__ */
3
+ return r("svg",{xmlns:"http://www.w3.org/2000/svg",width:"1em",height:"1em",viewBox:"0 0 24 24",className:e,"aria-hidden":"true",focusable:"false",children:/* @__PURE__ */r("path",{fill:"currentColor",d:"M7 21q-.825 0-1.412-.587T5 19V6q-.425 0-.712-.288T4 5t.288-.712T5 4h4q0-.425.288-.712T10 3h4q.425 0 .713.288T15 4h4q.425 0 .713.288T20 5t-.288.713T19 6v13q0 .825-.587 1.413T17 21zM17 6H7v13h10zm-7 11q.425 0 .713-.288T11 16V9q0-.425-.288-.712T10 8t-.712.288T9 9v7q0 .425.288.713T10 17m4 0q.425 0 .713-.288T15 16V9q0-.425-.288-.712T14 8t-.712.288T13 9v7q0 .425.288.713T14 17M7 6v13z"})})}var R={[m.APPROVED]:{color:"text-success",bgColor:"bg-success/20",icon:/* @__PURE__ */r(M,{className:"size-full"}),title:"Approved",description:"Your upload has been approved and is now visible to everyone viewing this event."},[m.IN_REVIEW]:{color:"text-warning",bgColor:"bg-warning/20",icon:/* @__PURE__ */r(_,{className:"size-full"}),title:"In Review",description:"Your upload is currently being reviewed. It will be visible once approved by the event admin."},[m.REJECTED]:{color:"text-destructive",bgColor:"bg-destructive/20",icon:/* @__PURE__ */r(q,{className:"size-full"}),title:"Rejected",description:"Your upload was not approved as it may not meet the content guidelines. Please upload appropriate content."}},F=e=>{const t=new Date(e).getTime();if(Number.isNaN(t))return"";const a=Math.max(0,Math.floor((Date.now()-t)/1e3)),n=[[60,"second"],[60,"minute"],[24,"hour"],[30,"day"],[12,"month"],[Number.POSITIVE_INFINITY,"year"]];let r=a,o="second";for(const[s,i]of n){if(r<s){o=i;break}r=Math.floor(r/s),o=i}return"second"===o&&r<30?"just now":`${r} ${o}${1===r?"":"s"} ago`},V=({content:e,onDelete:t,onStatusClick:a})=>{const n=e.status,o=R[n];/* @__PURE__ */
4
4
  return r("div",{className:"group relative size-full overflow-hidden sm:rounded",children:/* @__PURE__ */r("div",{className:"relative size-full",children:[
5
- /* @__PURE__ */r(C,{url:e.url||"",alt:e.caption||"",thumbnailUrl:e.thumbnailUrl||void 0,blurhash:e.blurhash||void 0,pswpData:{id:e._id,type:e.type,hash:e._id,width:e.dimension?.width,height:e.dimension?.height,thumbnailUrl:e.thumbnailUrl||void 0,caption:e.caption||""}}),
6
- /* @__PURE__ */r("div",{className:"pointer-events-none absolute inset-x-0 bottom-0 flex items-center justify-between bg-gradient-to-t from-black/50 to-transparent pl-3",children:[/* @__PURE__ */r(w,{variant:"caption",className:"text-xs text-white opacity-90 sm:text-sm",children:F(e.createdAt)}),/* @__PURE__ */r(M,{icon:/* @__PURE__ */r(L,{className:"size-5"}),color:"error",size:"sm",className:"pointer-events-auto","aria-label":"Delete upload",tooltip:"Delete",onClick:e=>{e.stopPropagation(),e.preventDefault(),t()}})]}),
7
- /* @__PURE__ */r("button",{type:"button","aria-label":`Status: ${o?.title||"Unknown"}`,onClick:e=>{e.stopPropagation(),e.preventDefault(),a(n)},className:c("pointer-events-auto absolute top-1 right-1 cursor-pointer rounded-full p-1 transition-opacity hover:opacity-80",o?.bgColor||"bg-gray-500/20"),children:/* @__PURE__ */r("span",{className:c("block size-5",o?.color||"text-gray-500"),children:o?.icon||null})})]})})},B=e=>new Date(e).toLocaleDateString("en-US",{month:"short",year:"numeric"}),K=e=>{if(0===e.length)return"";if(1===e.length)return B(e[0].createdAt);const{minTime:t,maxTime:a}=e.reduce((e,t)=>{const a=new Date(t.createdAt).getTime();return{minTime:Math.min(e.minTime,a),maxTime:Math.max(e.maxTime,a)}},{minTime:1/0,maxTime:-1/0}),n=B(new Date(t).toISOString()),r=B(new Date(a).toISOString());return n===r?n:`${n} - ${r}`},Y=()=>{const s=y(),m=N(),{user:v,loadingUser:C,portalContainer:U}=d(),_=t(null),M=t(null),O=t(null),[k,L]=a(null),[F,B]=a(null),{contents:Y,loading:Q,hasNextPage:G,fetchNextPage:H,isFetchingNextPage:J}=function({sort:e="-createdAt",limit:t=20}={}){const{user:a,loadingUser:r,props:o}=d(),s=o?.organizationId,c=a?.token,m=a?._id??a?.uid??null,{data:f,isLoading:v,isError:x,error:N,isFetchingNextPage:y,hasNextPage:w,fetchNextPage:b,refetch:T}=h({queryKey:[l.USER_UPLOADS,s??"",m,t,e],queryFn:async({pageParam:a=1})=>await i.get(u.MY_UPLOADS,{searchParams:{page:a,limit:t,sort:e},headers:{"x-org-id":s,...c?{Authorization:`Bearer ${c}`}:{}}}).json(),initialPageParam:1,getPreviousPageParam:p,getNextPageParam:g,staleTime:12e4,gcTime:3e5,enabled:!r&&!!a});return{contents:n(()=>f?.pages?.flatMap(e=>e?.data??[])??[],[f]),loading:v,isError:x,error:N,isFetchingNextPage:y,hasNextPage:!!w,fetchNextPage:b,refetch:T,totalCount:f?.pages?.[0]?.meta?.totalCount??0}}({sort:"-createdAt",limit:20});e(()=>{C||v||s("/auth/login",{state:{referrer:m.pathname},replace:!0})},[C,v]);const W=Y.length>0,Z=!Q&&!W,X=n(()=>{if(!Y.length)return[];const e={};return Y.forEach(t=>{const a=t.eventId||"unknown";e[a]||(e[a]={eventId:a,eventName:t.event?.name||"Event",uploads:[]}),e[a].uploads.push(t)}),Object.values(e).sort((e,t)=>{const a=new Date(e.uploads[0]?.createdAt||0).getTime();return new Date(t.uploads[0]?.createdAt||0).getTime()-a})},[Y]);e(()=>{const e=M.current;if(!e)return;const t=new P({gallery:e,children:"a",counter:!1,bgOpacity:1,wheelToZoom:!0,pinchToClose:!0,zoom:!1,pswpModule:j,appendToEl:U??document.body});return _.current=t,new x(t),new f(t),t.init(),()=>{t.destroy(),_.current=null}},[U]),e(()=>{const e=O.current;if(!e||!G)return;const t=new IntersectionObserver(e=>{e[0]?.isIntersecting&&G&&!J&&H()},{rootMargin:"200px"});return t.observe(e),()=>t.disconnect()},[G,J,H]);const ee=n(()=>Y.find(e=>e._id===k)??null,[k,Y]),te=F?R[F]:null;/* @__PURE__ */
5
+ /* @__PURE__ */r(P,{url:e.url||"",alt:e.caption||"",thumbnailUrl:e.thumbnailUrl||void 0,blurhash:e.blurhash||void 0,pswpData:{id:e._id,type:e.type,hash:e._id,width:e.dimension?.width,height:e.dimension?.height,thumbnailUrl:e.thumbnailUrl||void 0,caption:e.caption||""}}),
6
+ /* @__PURE__ */r("div",{className:"pointer-events-none absolute inset-x-0 bottom-0 flex items-center justify-between bg-gradient-to-t from-black/50 to-transparent pl-3",children:[/* @__PURE__ */r(w,{variant:"caption",className:"text-xs text-white opacity-90 sm:text-sm",children:F(e.createdAt)}),/* @__PURE__ */r(k,{icon:/* @__PURE__ */r(L,{className:"size-5"}),color:"error",size:"sm",className:"pointer-events-auto","aria-label":"Delete upload",tooltip:"Delete",onClick:e=>{e.stopPropagation(),e.preventDefault(),t()}})]}),
7
+ /* @__PURE__ */r("button",{type:"button","aria-label":`Status: ${o?.title||"Unknown"}`,onClick:e=>{e.stopPropagation(),e.preventDefault(),a(n)},className:c("pointer-events-auto absolute top-1 right-1 cursor-pointer rounded-full p-1 transition-opacity hover:opacity-80",o?.bgColor||"bg-gray-500/20"),children:/* @__PURE__ */r("span",{className:c("block size-5",o?.color||"text-gray-500"),children:o?.icon||null})})]})})},B=e=>new Date(e).toLocaleDateString("en-US",{month:"short",year:"numeric"}),K=e=>{if(0===e.length)return"";if(1===e.length)return B(e[0].createdAt);const{minTime:t,maxTime:a}=e.reduce((e,t)=>{const a=new Date(t.createdAt).getTime();return{minTime:Math.min(e.minTime,a),maxTime:Math.max(e.maxTime,a)}},{minTime:1/0,maxTime:-1/0}),n=B(new Date(t).toISOString()),r=B(new Date(a).toISOString());return n===r?n:`${n} - ${r}`},Y=()=>{const s=y(),m=N(),{user:v,loadingUser:P,portalContainer:q}=d(),_=t(null),k=t(null),O=t(null),[M,L]=a(null),[F,B]=a(null),{contents:Y,loading:Q,hasNextPage:G,fetchNextPage:J,isFetchingNextPage:H}=function({sort:e="-createdAt",limit:t=20}={}){const{user:a,loadingUser:r,props:o}=d(),s=o?.organizationId,c=a?.token,m=a?._id??a?.uid??null,{data:f,isLoading:v,isError:x,error:N,isFetchingNextPage:y,hasNextPage:w,fetchNextPage:b,refetch:T}=h({queryKey:[l.USER_UPLOADS,s??"",m,t,e],queryFn:async({pageParam:a=1})=>await i.get(u.MY_UPLOADS,{searchParams:{page:a,limit:t,sort:e},headers:{"x-org-id":s,...c?{Authorization:`Bearer ${c}`}:{}}}).json(),initialPageParam:1,getPreviousPageParam:p,getNextPageParam:g,staleTime:12e4,gcTime:3e5,enabled:!r&&!!a});return{contents:n(()=>f?.pages?.flatMap(e=>e?.data??[])??[],[f]),loading:v,isError:x,error:N,isFetchingNextPage:y,hasNextPage:!!w,fetchNextPage:b,refetch:T,totalCount:f?.pages?.[0]?.meta?.totalCount??0}}({sort:"-createdAt",limit:20});e(()=>{P||v||s("/auth/login",{state:{referrer:m.pathname},replace:!0})},[P,v]);const W=Y.length>0,Z=!Q&&!W,X=n(()=>{if(!Y.length)return[];const e={};return Y.forEach(t=>{const a=t.eventId||"unknown";e[a]||(e[a]={eventId:a,eventName:t.event?.name||"Event",uploads:[]}),e[a].uploads.push(t)}),Object.values(e).sort((e,t)=>{const a=new Date(e.uploads[0]?.createdAt||0).getTime();return new Date(t.uploads[0]?.createdAt||0).getTime()-a})},[Y]);e(()=>{const e=k.current;if(!e)return;const t=new D({gallery:e,children:"a",counter:!1,bgOpacity:1,wheelToZoom:!0,pinchToClose:!0,zoom:!1,pswpModule:C,appendToEl:q??document.body});return _.current=t,new x(t),new f(t),t.init(),()=>{t.destroy(),_.current=null}},[q,W]),e(()=>{const e=O.current;if(!e||!G)return;const t=new IntersectionObserver(e=>{e[0]?.isIntersecting&&G&&!H&&J()},{rootMargin:"200px"});return t.observe(e),()=>t.disconnect()},[G,H,J]);const ee=n(()=>Y.find(e=>e._id===M)??null,[M,Y]),te=F?R[F]:null;/* @__PURE__ */
8
8
  return r("div",{className:"bg-background mx-auto min-h-screen max-w-5xl px-3 pt-16 pb-12 lg:px-0",children:[
9
- /* @__PURE__ */r("div",{className:"relative mb-6 flex items-center justify-center",children:[/* @__PURE__ */r(I,{}),/* @__PURE__ */r(w,{variant:"h4",align:"center",children:"My Uploads"})]}),
9
+ /* @__PURE__ */r("div",{className:"relative mb-6 flex items-center justify-center",children:[/* @__PURE__ */r(j,{}),/* @__PURE__ */r(w,{variant:"h4",align:"center",children:"My Uploads"})]}),
10
10
  /* @__PURE__ */r("section",{className:"space-y-3",children:Q?/* @__PURE__ */r("div",{className:"space-y-8 py-4",children:Array.from({length:2}).map((e,t)=>/* @__PURE__ */r("div",{className:"space-y-4",children:[/* @__PURE__ */r("div",{className:"flex items-center justify-between",children:[/* @__PURE__ */r(T,{className:"h-6 w-48"}),/* @__PURE__ */r(T,{className:"h-5 w-24"})]}),/* @__PURE__ */r("div",{className:"grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-4",children:Array.from({length:4}).map((e,t)=>/* @__PURE__ */r(T,{className:"h-52 w-full lg:h-64"},t))})]},t))}):Z?/* @__PURE__ */r(b,{className:"py-12",title:"No uploads yet",description:"Photos and videos you upload to events will appear here."}):/* @__PURE__ */r(o,{children:[
11
- /* @__PURE__ */r("div",{ref:M,className:"space-y-8 py-4",children:X.map(e=>/* @__PURE__ */r("div",{className:"space-y-4",children:[/* @__PURE__ */r("div",{className:"flex items-center justify-between",children:[/* @__PURE__ */r(w,{variant:"h5",color:"muted",children:e.eventName}),/* @__PURE__ */r("span",{className:"text-muted-foreground text-sm font-light uppercase sm:text-base",children:K(e.uploads)})]}),/* @__PURE__ */r("div",{className:"grid grid-cols-2 gap-1 sm:grid-cols-3 sm:gap-3 lg:grid-cols-4",children:e.uploads.map(e=>/* @__PURE__ */r("div",{className:"h-52 lg:h-64",children:/* @__PURE__ */r(V,{content:e,onDelete:()=>L(e._id),onStatusClick:e=>B(e)})},e._id))})]},e.eventId))}),
12
- /* @__PURE__ */r("div",{ref:O,className:"h-px w-full"}),J&&/* @__PURE__ */r("div",{className:"my-6 w-full",children:/* @__PURE__ */r(q,{})})]})}),
11
+ /* @__PURE__ */r("div",{ref:k,className:"space-y-8 py-4",children:X.map(e=>/* @__PURE__ */r("div",{className:"space-y-4",children:[/* @__PURE__ */r("div",{className:"flex items-center justify-between",children:[/* @__PURE__ */r(w,{variant:"h5",color:"muted",children:e.eventName}),/* @__PURE__ */r("span",{className:"text-muted-foreground text-sm font-light uppercase sm:text-base",children:K(e.uploads)})]}),/* @__PURE__ */r("div",{className:"grid grid-cols-2 gap-1 sm:grid-cols-3 sm:gap-3 lg:grid-cols-4",children:e.uploads.map(e=>/* @__PURE__ */r("div",{className:"h-52 lg:h-64",children:/* @__PURE__ */r(V,{content:e,onDelete:()=>L(e._id),onStatusClick:e=>B(e)})},e._id))})]},e.eventId))}),
12
+ /* @__PURE__ */r("div",{ref:O,className:"h-px w-full"}),H&&/* @__PURE__ */r("div",{className:"my-6 w-full",children:/* @__PURE__ */r(E,{})})]})}),
13
13
  /* @__PURE__ */r($,{content:ee,onClose:()=>L(null),onDeleted:()=>L(null)}),
14
- /* @__PURE__ */r(A,{open:Boolean(F),onOpenChange:e=>!e&&B(null),children:/* @__PURE__ */r(E,{className:"w-full max-w-lg",children:[/* @__PURE__ */r("div",{className:"flex flex-col gap-4 p-2 sm:flex-row sm:items-start sm:p-4",children:[/* @__PURE__ */r("div",{className:c("mx-auto flex size-12 shrink-0 items-center justify-center rounded-full sm:mx-0 sm:size-10",te?.bgColor||"bg-gray-500/20"),children:/* @__PURE__ */r("span",{className:c("size-6",te?.color||"text-gray-500"),children:te?.icon||null})}),/* @__PURE__ */r("div",{className:"flex-1 space-y-2 text-center sm:text-left",children:[/* @__PURE__ */r(S,{children:te?.title||"Unknown Status"}),/* @__PURE__ */r(z,{children:te?.description||"Status information unavailable."})]})]}),/* @__PURE__ */r("div",{className:"flex justify-end p-4",children:/* @__PURE__ */r(D,{variant:"outlined",color:"foreground",onClick:()=>B(null),children:"Got it"})})]})})]})};export{Y as default};
14
+ /* @__PURE__ */r(U,{open:Boolean(F),onOpenChange:e=>!e&&B(null),children:/* @__PURE__ */r(S,{className:"w-full max-w-lg",children:[/* @__PURE__ */r("div",{className:"flex flex-col gap-4 p-2 sm:flex-row sm:items-start sm:p-4",children:[/* @__PURE__ */r("div",{className:c("mx-auto flex size-12 shrink-0 items-center justify-center rounded-full sm:mx-0 sm:size-10",te?.bgColor||"bg-gray-500/20"),children:/* @__PURE__ */r("span",{className:c("size-6",te?.color||"text-gray-500"),children:te?.icon||null})}),/* @__PURE__ */r("div",{className:"flex-1 space-y-2 text-center sm:text-left",children:[/* @__PURE__ */r(z,{children:te?.title||"Unknown Status"}),/* @__PURE__ */r(A,{children:te?.description||"Status information unavailable."})]})]}),/* @__PURE__ */r("div",{className:"flex justify-end p-4",children:/* @__PURE__ */r(I,{variant:"outlined",color:"foreground",onClick:()=>B(null),children:"Got it"})})]})})]})};export{Y as default};