@myrjfa/ui 1.1.1 → 1.1.2

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.
Files changed (50) hide show
  1. package/dist/chat/ChatMain.d.ts +3 -1
  2. package/dist/chat/ChatMain.d.ts.map +1 -1
  3. package/dist/chat/ChatMain.js +2 -1
  4. package/dist/chat/MessageBubble.js +1 -1
  5. package/dist/chat/MessageInput.d.ts +1 -1
  6. package/dist/chat/MessageInput.d.ts.map +1 -1
  7. package/dist/chat/components/ParticipantSelector.d.ts +1 -1
  8. package/dist/chat/components/ParticipantSelector.d.ts.map +1 -1
  9. package/dist/dashboard/app-sidebar.js +1 -1
  10. package/dist/dashboard/data-table.d.ts +2 -1
  11. package/dist/dashboard/data-table.d.ts.map +1 -1
  12. package/dist/dashboard/data-table.js +3 -3
  13. package/dist/index.d.ts +5 -0
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +6 -0
  16. package/dist/main/navbar.js +3 -3
  17. package/dist/main/review-page.js +1 -1
  18. package/dist/misc/document-upload-button.d.ts +12 -0
  19. package/dist/misc/document-upload-button.d.ts.map +1 -0
  20. package/dist/misc/document-upload-button.js +27 -0
  21. package/dist/misc/empty-state.d.ts +10 -0
  22. package/dist/misc/empty-state.d.ts.map +1 -0
  23. package/dist/misc/empty-state.js +5 -0
  24. package/dist/misc/image-compressor.d.ts +29 -0
  25. package/dist/misc/image-compressor.d.ts.map +1 -0
  26. package/dist/misc/image-compressor.js +70 -0
  27. package/dist/misc/input.d.ts.map +1 -1
  28. package/dist/misc/input.js +1 -1
  29. package/dist/misc/multiImageUploader.d.ts +2 -1
  30. package/dist/misc/multiImageUploader.d.ts.map +1 -1
  31. package/dist/misc/multiImageUploader.js +26 -2
  32. package/dist/misc/newsLetter.js +1 -1
  33. package/dist/misc/notification.js +1 -1
  34. package/dist/misc/pwa-install-button.d.ts +9 -0
  35. package/dist/misc/pwa-install-button.d.ts.map +1 -0
  36. package/dist/misc/pwa-install-button.js +158 -0
  37. package/dist/misc/skeleton.d.ts +36 -1
  38. package/dist/misc/skeleton.d.ts.map +1 -1
  39. package/dist/misc/skeleton.js +39 -2
  40. package/dist/misc/tile.js +1 -1
  41. package/dist/misc/whatsapp-help.d.ts +9 -0
  42. package/dist/misc/whatsapp-help.d.ts.map +1 -0
  43. package/dist/misc/whatsapp-help.js +13 -0
  44. package/package.json +5 -4
  45. package/dist/chat/dialogs/UserProfileDialog.d.ts +0 -10
  46. package/dist/chat/dialogs/UserProfileDialog.d.ts.map +0 -1
  47. package/dist/chat/dialogs/UserProfileDialog.js +0 -100
  48. package/dist/main/portfolio/temp-page.d.ts +0 -2
  49. package/dist/main/portfolio/temp-page.d.ts.map +0 -1
  50. package/dist/main/portfolio/temp-page.js +0 -102
@@ -1,9 +1,11 @@
1
1
  import type { Conversation } from "@myrjfa/state/dist/lib/models/chat";
2
+ import { UserRole } from "@myrjfa/state/dist/lib/models/props";
2
3
  interface ChatMainProps {
3
4
  conversation: Conversation;
4
5
  currentUserId: string;
5
6
  onBack: () => void;
7
+ onCallAlert?: (targetId: string, targetRole: UserRole) => void;
6
8
  }
7
- export declare function ChatMain({ conversation, currentUserId, onBack, }: ChatMainProps): import("react/jsx-runtime").JSX.Element;
9
+ export declare function ChatMain({ conversation, currentUserId, onBack, onCallAlert, }: ChatMainProps): import("react/jsx-runtime").JSX.Element;
8
10
  export {};
9
11
  //# sourceMappingURL=ChatMain.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ChatMain.d.ts","sourceRoot":"","sources":["../../src/chat/ChatMain.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,YAAY,EAAW,MAAM,oCAAoC,CAAC;AAchF,UAAU,aAAa;IACnB,YAAY,EAAE,YAAY,CAAC;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,wBAAgB,QAAQ,CAAC,EACrB,YAAY,EACZ,aAAa,EACb,MAAM,GACT,EAAE,aAAa,2CA2iBf"}
1
+ {"version":3,"file":"ChatMain.d.ts","sourceRoot":"","sources":["../../src/chat/ChatMain.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,YAAY,EAAW,MAAM,oCAAoC,CAAC;AAahF,OAAO,EAAE,QAAQ,EAAE,MAAM,qCAAqC,CAAC;AAE/D,UAAU,aAAa;IACnB,YAAY,EAAE,YAAY,CAAC;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,WAAW,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,KAAK,IAAI,CAAC;CAClE;AAED,wBAAgB,QAAQ,CAAC,EACrB,YAAY,EACZ,aAAa,EACb,MAAM,EACN,WAAW,GACd,EAAE,aAAa,2CA4iBf"}
@@ -19,7 +19,7 @@ import { Button } from "../misc/button";
19
19
  import { confirmActions, deleteActions, showAlert } from "../misc/alertdialogclaude";
20
20
  import { userApi } from "@myrjfa/state/dist/lib/actions/user";
21
21
  import { checkPhonePermission } from "../utils/permissions";
22
- export function ChatMain({ conversation, currentUserId, onBack, }) {
22
+ export function ChatMain({ conversation, currentUserId, onBack, onCallAlert, }) {
23
23
  var _a, _b, _c, _d, _e;
24
24
  const { socket, messages, setMessages, sendMessage, typingUsers, onlineUsers, editMessage, toggleMessageReaction, deleteMessage, markRead, createPoll, votePoll, refreshConversations } = useChatContext();
25
25
  const messagesEndRef = useRef(null);
@@ -203,6 +203,7 @@ export function ChatMain({ conversation, currentUserId, onBack, }) {
203
203
  return;
204
204
  }
205
205
  if (otherUser) {
206
+ onCallAlert === null || onCallAlert === void 0 ? void 0 : onCallAlert(otherUser.userId, otherUser.role);
206
207
  // Initiate tel: link (will prompt to call on mobile/desktop)
207
208
  window.location.href = `tel:${otherUser.userId}`;
208
209
  }
@@ -6,7 +6,7 @@ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigge
6
6
  import { Button } from "../misc/button";
7
7
  import { useEffect, useState, useRef, useCallback, useMemo } from "react";
8
8
  import EmojiPicker from 'emoji-picker-react';
9
- import { chatApi } from "@myrjfa/state";
9
+ import { chatApi } from "@myrjfa/state/dist/lib/actions/chat";
10
10
  const QUICK_REACTIONS = [
11
11
  { emoji: "❤️", label: "love" },
12
12
  { emoji: "👍", label: "like" },
@@ -1,4 +1,4 @@
1
- import { Message } from "@myrjfa/state";
1
+ import { Message } from "@myrjfa/state/dist/lib/models/chat";
2
2
  interface MessageInputProps {
3
3
  onSend: (text: string, files?: File[]) => void;
4
4
  onTypingStart?: () => void;
@@ -1 +1 @@
1
- {"version":3,"file":"MessageInput.d.ts","sourceRoot":"","sources":["../../src/chat/MessageInput.tsx"],"names":[],"mappings":"AAcA,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAExC,UAAU,iBAAiB;IACvB,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC;IAC/C,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,GAAG,CAAC;IACd,WAAW,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAC7B,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACnD,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,aAAa,EAAE,OAAO,KAAK,IAAI,CAAC;IACrF,YAAY,CAAC,EAAE,GAAG,EAAE,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,GAAG,YAAY,GAAG,UAAU,CAAC;CACpD;AAED,wBAAgB,YAAY,CAAC,EACzB,MAAM,EACN,aAAa,EACb,YAAY,EACZ,QAAQ,EACR,OAAO,EACP,WAAW,EACX,MAAM,EACN,YAAY,EACZ,aAAa,EACb,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,YAAiB,EAAE,QAAQ;AAC3B,WAAoB,EACvB,EAAE,iBAAiB,2CAobnB"}
1
+ {"version":3,"file":"MessageInput.d.ts","sourceRoot":"","sources":["../../src/chat/MessageInput.tsx"],"names":[],"mappings":"AAcA,OAAO,EAAE,OAAO,EAAE,MAAM,oCAAoC,CAAC;AAE7D,UAAU,iBAAiB;IACvB,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC;IAC/C,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,GAAG,CAAC;IACd,WAAW,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAC7B,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACnD,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,aAAa,EAAE,OAAO,KAAK,IAAI,CAAC;IACrF,YAAY,CAAC,EAAE,GAAG,EAAE,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,GAAG,YAAY,GAAG,UAAU,CAAC;CACpD;AAED,wBAAgB,YAAY,CAAC,EACzB,MAAM,EACN,aAAa,EACb,YAAY,EACZ,QAAQ,EACR,OAAO,EACP,WAAW,EACX,MAAM,EACN,YAAY,EACZ,aAAa,EACb,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,YAAiB,EAAE,QAAQ;AAC3B,WAAoB,EACvB,EAAE,iBAAiB,2CAobnB"}
@@ -1,4 +1,4 @@
1
- import { ExpenseSplit } from "@myrjfa/state";
1
+ import { ExpenseSplit } from "@myrjfa/state/dist/lib/models/chat";
2
2
  import { Dispatch, SetStateAction } from "react";
3
3
  interface ParticipantSelectorProps {
4
4
  participants: string[];
@@ -1 +1 @@
1
- {"version":3,"file":"ParticipantSelector.d.ts","sourceRoot":"","sources":["../../../src/chat/components/ParticipantSelector.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC;AAEjD,UAAU,wBAAwB;IAC9B,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,QAAQ,EAAE,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAC3C,cAAc,EAAE,QAAQ,CAAC,cAAc,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC;IACzD,cAAc,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED,wBAAgB,mBAAmB,CAAC,EAChC,YAAY,EACZ,QAAQ,EACR,cAAc,EACd,cAAc,EACjB,EAAE,wBAAwB,2CA2D1B"}
1
+ {"version":3,"file":"ParticipantSelector.d.ts","sourceRoot":"","sources":["../../../src/chat/components/ParticipantSelector.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAClE,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC;AAEjD,UAAU,wBAAwB;IAC9B,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,QAAQ,EAAE,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAC3C,cAAc,EAAE,QAAQ,CAAC,cAAc,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC;IACzD,cAAc,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED,wBAAgB,mBAAmB,CAAC,EAChC,YAAY,EACZ,QAAQ,EACR,cAAc,EACd,cAAc,EACjB,EAAE,wBAAwB,2CA2D1B"}
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
- import { logoutUser } from "@myrjfa/state/dist/lib/auth";
3
+ import { logoutUser } from "@myrjfa/state/dist/lib/actions/auth";
4
4
  import { isLoggedInAtom, userAtom, userLoadingAtom } from "@myrjfa/state/dist/lib/userAtom";
5
5
  import { useAtom, useAtomValue, useSetAtom } from "jotai";
6
6
  import { FileText, HelpCircleIcon, Inbox, LayoutDashboardIcon, LogOutIcon, Package, User } from "lucide-react";
@@ -10,10 +10,11 @@ interface DataTableProps<T> {
10
10
  columns: Column<T>[];
11
11
  actions?: (item: T) => JSX.Element;
12
12
  onRowClick?: (id: string) => void;
13
+ getRowClass?: (item: T) => string;
13
14
  }
14
15
  export declare function DataTable<T extends {
15
16
  slug?: string;
16
17
  _id?: string;
17
- }>({ data, columns, actions, onRowClick, }: DataTableProps<T>): import("react/jsx-runtime").JSX.Element;
18
+ }>({ data, columns, actions, onRowClick, getRowClass, }: DataTableProps<T>): import("react/jsx-runtime").JSX.Element;
18
19
  export {};
19
20
  //# sourceMappingURL=data-table.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"data-table.d.ts","sourceRoot":"","sources":["../../src/dashboard/data-table.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,EAAE,GAAG,EAAqB,MAAM,OAAO,CAAC;AAYtD,MAAM,WAAW,MAAM,CAAC,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC,CAAC;IACrB,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,KAAK,CAAC,SAAS,CAAC;IACpC,QAAQ,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,UAAU,cAAc,CAAC,CAAC;IACtB,IAAI,EAAE,CAAC,EAAE,CAAC;IACV,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IACrB,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,GAAG,CAAC,OAAO,CAAC;IACnC,UAAU,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;CACrC;AAED,wBAAgB,SAAS,CAAC,CAAC,SAAS;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,EACjE,IAAI,EACJ,OAAO,EACP,OAAO,EACP,UAAU,GACb,EAAE,cAAc,CAAC,CAAC,CAAC,2CAuNnB"}
1
+ {"version":3,"file":"data-table.d.ts","sourceRoot":"","sources":["../../src/dashboard/data-table.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,EAAE,GAAG,EAAqB,MAAM,OAAO,CAAC;AAYtD,MAAM,WAAW,MAAM,CAAC,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC,CAAC;IACrB,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,KAAK,CAAC,SAAS,CAAC;IACpC,QAAQ,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,UAAU,cAAc,CAAC,CAAC;IACtB,IAAI,EAAE,CAAC,EAAE,CAAC;IACV,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IACrB,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,GAAG,CAAC,OAAO,CAAC;IACnC,UAAU,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,CAAC;CACrC;AAED,wBAAgB,SAAS,CAAC,CAAC,SAAS;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,EACjE,IAAI,EACJ,OAAO,EACP,OAAO,EACP,UAAU,EACV,WAAW,GACd,EAAE,cAAc,CAAC,CAAC,CAAC,2CAuNnB"}
@@ -3,7 +3,7 @@ import { ChevronDown, ChevronUp, Inbox } from 'lucide-react';
3
3
  import { useMemo, useState } from 'react';
4
4
  import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '../dashboard/table';
5
5
  import { Input } from '../misc/input';
6
- export function DataTable({ data, columns, actions, onRowClick, }) {
6
+ export function DataTable({ data, columns, actions, onRowClick, getRowClass, }) {
7
7
  const [searchQuery, setSearchQuery] = useState('');
8
8
  const [sortColumn, setSortColumn] = useState(null);
9
9
  const [sortDirection, setSortDirection] = useState('asc');
@@ -40,14 +40,14 @@ export function DataTable({ data, columns, actions, onRowClick, }) {
40
40
  };
41
41
  return (_jsxs("div", { className: "w-full", children: [_jsx("div", { className: "p-2", children: _jsx(Input, { placeholder: "Search...", className: "mb-4 max-w-sm", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value) }) }), _jsx("div", { className: "hidden lg:block overflow-auto", children: _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [columns.map((column) => (_jsx(TableHead, { className: "cursor-pointer select-none", onClick: () => column.sortable && toggleSort(column.accessorKey), children: _jsxs("div", { className: "flex items-center gap-1", children: [column.header, column.sortable && (sortColumn === column.accessorKey ? (sortDirection === 'asc' ? (_jsx(ChevronUp, { className: "w-4 h-4 text-black" })) : (_jsx(ChevronDown, { className: "w-4 h-4 text-black" }))) : (_jsxs("div", { className: "flex flex-col text-gray-400 leading-none", children: [_jsx(ChevronUp, { className: "w-3 h-3 -mb-1" }), _jsx(ChevronDown, { className: "w-3 h-3" })] })))] }) }, column.accessorKey))), actions && _jsx(TableHead, { children: "Actions" })] }) }), _jsx(TableBody, { children: sortedData.length === 0 ? (_jsx(TableRow, { children: _jsx(TableCell, { colSpan: columns.length + (actions ? 1 : 0), className: "h-40 text-center text-gray-500", children: _jsxs("div", { className: "flex flex-col items-center justify-center gap-2", children: [_jsx(Inbox, { className: "w-8 h-8 text-gray-400" }), _jsx("span", { className: "text-base font-medium", children: "No data available" })] }) }) })) : (sortedData.map((item) => {
42
42
  const key = item.slug || item._id || Math.random().toString();
43
- return (_jsxs(TableRow, { onClick: () => onRowClick && onRowClick(key), className: onRowClick ? 'hover:bg-accent cursor-pointer' : '', children: [columns.map((column) => (_jsx(TableCell, { children: column.cell
43
+ return (_jsxs(TableRow, { onClick: () => onRowClick && onRowClick(key), className: `${onRowClick ? 'hover:bg-accent cursor-pointer' : ''} ${getRowClass ? getRowClass(item) : ''}`, children: [columns.map((column) => (_jsx(TableCell, { children: column.cell
44
44
  ? column.cell(item)
45
45
  : item[column.accessorKey] }, column.accessorKey))), actions && (_jsx(TableCell, { onClick: (e) => e.stopPropagation(), children: actions(item) }))] }, key));
46
46
  })) })] }) }), _jsx("div", { className: "lg:hidden", children: _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsx(TableRow, { children: columns.slice(1).map((column) => (_jsx(TableHead, { className: "cursor-pointer select-none", onClick: () => column.sortable && toggleSort(column.accessorKey), children: _jsxs("div", { className: "flex items-center gap-1", children: [column.header, column.sortable && (sortColumn === column.accessorKey ? (sortDirection === 'asc' ? (_jsx(ChevronUp, { className: "w-4 h-4 text-black" })) : (_jsx(ChevronDown, { className: "w-4 h-4 text-black" }))) : (_jsxs("div", { className: "flex flex-col text-gray-400 leading-none", children: [_jsx(ChevronUp, { className: "w-3 h-3 -mb-1" }), _jsx(ChevronDown, { className: "w-3 h-3" })] })))] }) }, column.accessorKey))) }) }), _jsx(TableBody, { children: sortedData.length === 0 ? (_jsx(TableRow, { children: _jsx(TableCell, { colSpan: columns.length + (actions ? 1 : 0), className: "h-40 text-center text-gray-500", children: _jsxs("div", { className: "flex flex-col items-center justify-center gap-2", children: [_jsx(Inbox, { className: "w-8 h-8 text-gray-400" }), _jsx("span", { className: "text-base font-medium", children: "No data available" })] }) }) })) : (sortedData.map((item) => {
47
47
  const key = item.slug || item._id || Math.random().toString();
48
48
  return (_jsxs(_Fragment, { children: [_jsx(TableRow, { className: "bg-gray-100", children: _jsx(TableCell, { colSpan: columns.length - 1, className: "py-3", children: _jsx("div", { className: "font-semibold text-gray-900", children: columns[0].cell
49
49
  ? columns[0].cell(item)
50
- : item[columns[0].accessorKey] }) }) }), _jsx(TableRow, { onClick: () => onRowClick && onRowClick(key), className: onRowClick ? 'hover:bg-accent cursor-pointer' : '', children: columns.slice(1).map((column) => (_jsx(TableCell, { children: column.cell
50
+ : item[columns[0].accessorKey] }) }) }), _jsx(TableRow, { onClick: () => onRowClick && onRowClick(key), className: `${onRowClick ? 'hover:bg-accent cursor-pointer' : ''} ${getRowClass ? getRowClass(item) : ''}`, children: columns.slice(1).map((column) => (_jsx(TableCell, { children: column.cell
51
51
  ? column.cell(item)
52
52
  : item[column.accessorKey] }, column.accessorKey))) }, key), actions && (_jsx(TableRow, { className: "bg-gray-30", children: _jsx(TableCell, { colSpan: columns.length - 1, className: "py-3", onClick: (e) => e.stopPropagation(), children: actions(item) }) }))] }));
53
53
  })) })] }) })] }));
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from './misc/button';
2
+ export * from './misc/empty-state';
2
3
  export * from './misc/accordion';
3
4
  export * from './misc/alert';
4
5
  export * from './misc/alertdialogclaude';
@@ -55,4 +56,8 @@ export * from './main/navbar';
55
56
  export * from './main/paginationBar';
56
57
  export * from './main/review-page';
57
58
  export * from './misc/image-cropper';
59
+ export * from './misc/pwa-install-button';
60
+ export * from './misc/whatsapp-help';
61
+ export * from './misc/image-compressor';
62
+ export * from './misc/skeleton';
58
63
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAC7B,cAAc,0BAA0B,CAAC;AACzC,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,sBAAsB,CAAC;AACrC,cAAc,YAAY,CAAC;AAC3B,cAAc,qBAAqB,CAAC;AACpC,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAChC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AACjC,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,4BAA4B,CAAC;AAC3C,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC;AACxC,cAAc,kCAAkC,CAAC;AACjD,cAAc,wBAAwB,CAAC;AACvC,cAAc,sBAAsB,CAAC;AACrC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,mBAAmB,CAAC;AAClC,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AACvC,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAC7B,cAAc,0BAA0B,CAAC;AACzC,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,sBAAsB,CAAC;AACrC,cAAc,YAAY,CAAC;AAC3B,cAAc,qBAAqB,CAAC;AACpC,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAChC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AACjC,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,4BAA4B,CAAC;AAC3C,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC;AACxC,cAAc,kCAAkC,CAAC;AACjD,cAAc,wBAAwB,CAAC;AACvC,cAAc,sBAAsB,CAAC;AACrC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,mBAAmB,CAAC;AAClC,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AACvC,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AAGrC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,iBAAiB,CAAC"}
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from './misc/button';
2
+ export * from './misc/empty-state';
2
3
  export * from './misc/accordion';
3
4
  export * from './misc/alert';
4
5
  export * from './misc/alertdialogclaude';
@@ -55,3 +56,8 @@ export * from './main/navbar';
55
56
  export * from './main/paginationBar';
56
57
  export * from './main/review-page';
57
58
  export * from './misc/image-cropper';
59
+ // New components — features #9, #10, #21, #26
60
+ export * from './misc/pwa-install-button';
61
+ export * from './misc/whatsapp-help';
62
+ export * from './misc/image-compressor';
63
+ export * from './misc/skeleton';
@@ -2,11 +2,11 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { Popover, PopoverContent, PopoverTrigger } from "@radix-ui/react-popover";
4
4
  import { Separator } from "@radix-ui/react-separator";
5
- import { logoutUser } from "@myrjfa/state/dist/lib/auth";
5
+ import { logoutUser } from "@myrjfa/state/dist/lib/actions/auth";
6
6
  import { isLoggedInAtom, userAtom, userLoadingAtom } from "@myrjfa/state/dist/lib/userAtom";
7
7
  import { opportunityTypeOptions } from "@myrjfa/state/dist/lib/models/opportunities/opportunity";
8
8
  import { useAtom, useAtomValue } from "jotai";
9
- import { ChevronDown, Globe, Heart, LayoutDashboardIcon, Store, Target, Luggage, MessageCircle } from "lucide-react";
9
+ import { ChevronDown, Globe, Heart, LayoutDashboardIcon, Store, Target, Luggage } from "lucide-react";
10
10
  import Link from "next/link";
11
11
  import { useRouter } from "next/navigation";
12
12
  import { useEffect, useState } from "react";
@@ -61,7 +61,7 @@ export default function Navbar({ dark = false }) {
61
61
  const navItemColor = dark
62
62
  ? "hover:text-white text-gray-250"
63
63
  : "hover:text-foreground text-gray-750";
64
- return (_jsx("header", { className: `fixed top-0 left-0 z-[100] mx-auto transition-all duration-300 w-full ${navbarBg} ${textColor} ${navbarBorder}`, children: _jsxs("div", { className: "mx-auto flex items-center justify-between gap-10 py-4 px-4 lg:px-8", children: [_jsxs(Link, { href: "/", className: "flex items-center gap-3", children: [_jsxs(Avatar, { className: "ml-2 -mr-4.5", children: [_jsx(AvatarImage, { src: "/images/logoicon.webp" }), _jsxs(AvatarFallback, { children: [(currentUser === null || currentUser === void 0 ? void 0 : currentUser.firstName.charAt(0)) || "D", (currentUser === null || currentUser === void 0 ? void 0 : currentUser.lastName.charAt(0)) || "P"] })] }), _jsx("span", { className: "font-heading text-xl font-bold", children: "rippeaze" })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs("nav", { className: "hidden md:flex items-center gap-10 justify-end", children: [_jsxs(Popover, { children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs("button", { className: `flex items-center gap-1 text-sm font-medium cursor-pointer ${navItemColor}`, children: [_jsx(Target, { className: "h-5 w-5" }), _jsx("span", { children: "Opportunities" }), _jsx(ChevronDown, { className: "h-4 w-4" })] }) }), _jsx(PopoverContent, { className: "w-56 p-2 mt-3 bg-white rounded-lg shadow-lg border", align: "start", sideOffset: 5, children: _jsx("div", { className: "flex flex-col", children: opportunityTypeOptions.map((opt) => (_jsxs(Link, { href: `/opportunities?type=${opt.value}`, className: "px-3 py-2 text-sm text-gray-700 hover:bg-gray-100 rounded-md flex flex-col", children: [_jsx("span", { className: "font-medium", children: opt.label }), _jsx("span", { className: "text-xs text-gray-500", children: opt.description })] }, opt.value))) }) })] }), _jsx(NavItem, { icon: _jsx(Luggage, { className: "h-5 w-5" }), label: "Packages", href: "/packages", className: navItemColor }), _jsx(NavItem, { icon: _jsx(Globe, { className: "h-5 w-5" }), label: "Blogs", href: "/blogs", className: navItemColor }), isLoggedIn && _jsx(NavItem, { icon: _jsx(MessageCircle, { className: "h-5 w-5" }), label: "Chat", href: "/chat", className: navItemColor, external: true }), currentUser ? _jsx(NavItem, { icon: _jsx(Store, { className: "h-5 w-5" }), label: "Become a Partner", href: "/contact", className: navItemColor })
64
+ return (_jsx("header", { className: `fixed top-0 left-0 z-[100] mx-auto transition-all duration-300 w-full ${navbarBg} ${textColor} ${navbarBorder}`, children: _jsxs("div", { className: "mx-auto flex items-center justify-between gap-10 py-4 px-4 lg:px-8", children: [_jsxs(Link, { href: "/", className: "flex items-center gap-3", children: [_jsxs(Avatar, { className: "ml-2 -mr-4.5", children: [_jsx(AvatarImage, { src: "/images/logoicon.webp" }), _jsxs(AvatarFallback, { children: [(currentUser === null || currentUser === void 0 ? void 0 : currentUser.firstName.charAt(0)) || "D", (currentUser === null || currentUser === void 0 ? void 0 : currentUser.lastName.charAt(0)) || "P"] })] }), _jsx("span", { className: "font-heading text-xl font-bold", children: "rippeaze" })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs("nav", { className: "hidden md:flex items-center gap-10 justify-end", children: [_jsxs(Popover, { children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs("button", { className: `flex items-center gap-1 text-sm font-medium cursor-pointer ${navItemColor}`, children: [_jsx(Target, { className: "h-5 w-5" }), _jsx("span", { children: "Opportunities" }), _jsx(ChevronDown, { className: "h-4 w-4" })] }) }), _jsx(PopoverContent, { className: "w-56 p-2 mt-3 bg-white rounded-lg shadow-lg border", align: "start", sideOffset: 5, children: _jsx("div", { className: "flex flex-col", children: opportunityTypeOptions.map((opt) => (_jsxs(Link, { href: `/opportunities?type=${opt.value}`, className: "px-3 py-2 text-sm text-gray-700 hover:bg-gray-100 rounded-md flex flex-col", children: [_jsx("span", { className: "font-medium", children: opt.label }), _jsx("span", { className: "text-xs text-gray-500", children: opt.description })] }, opt.value))) }) })] }), _jsx(NavItem, { icon: _jsx(Luggage, { className: "h-5 w-5" }), label: "Packages", href: "/packages", className: navItemColor }), _jsx(NavItem, { icon: _jsx(Globe, { className: "h-5 w-5" }), label: "Blogs", href: "/blogs", className: navItemColor }), currentUser ? _jsx(NavItem, { icon: _jsx(Store, { className: "h-5 w-5" }), label: "Become a Partner", href: "/contact", className: navItemColor })
65
65
  : _jsx(NavItem, { icon: _jsx(Store, { className: "h-5 w-5" }), label: "Register as Business", href: "/auth/register?role=host", className: navItemColor }), isLoggedIn && _jsx(NotificationBell, { navItemColor: navItemColor })] }), _jsx("div", { className: "hidden md:flex items-center gap-2", children: _jsxs(Popover, { children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx(Button, { variant: "link", className: "flex w-full gap-1 cursor-pointer", children: _jsxs(Avatar, { className: "ml-5 mr-4.5", children: [_jsx(AvatarImage, { src: (_a = currentUser === null || currentUser === void 0 ? void 0 : currentUser.profilePic) !== null && _a !== void 0 ? _a : "/images/profile_placeholder.webp" }), _jsxs(AvatarFallback, { children: [(currentUser === null || currentUser === void 0 ? void 0 : currentUser.firstName.charAt(0)) || "D", (currentUser === null || currentUser === void 0 ? void 0 : currentUser.lastName.charAt(0)) || "P"] })] }) }) }), _jsx(PopoverContent, { className: `w-full p-0 mt-3 bg-${dark ? "primary" : "white"} rounded-lg shadow-lg`, align: "end", sideOffset: 5, children: _jsxs("div", { className: "py-2 w-full", children: [_jsxs("div", { className: "px-4 py-3 border-b w-full", children: [_jsx("p", { className: "text-sm font-medium", children: (currentUser === null || currentUser === void 0 ? void 0 : currentUser.username) || "Guest" }), _jsx("p", { className: "text-xs text-gray-500", children: (currentUser === null || currentUser === void 0 ? void 0 : currentUser.email) || "user@example.com" })] }), _jsxs("div", { className: "flex flex-col", children: [isLoggedIn ?
66
66
  _jsxs(Link, { href: `/profile/${(currentUser === null || currentUser === void 0 ? void 0 : currentUser.role) || "user"}/${(currentUser === null || currentUser === void 0 ? void 0 : currentUser.username) || "Guest"}`, className: "w-full py-2 px-4 text-sm text-gray-700 hover:bg-gray-100 flex items-center gap-2", children: [_jsxs(Avatar, { className: "w-4 h-4", children: [_jsx(AvatarImage, { src: (_b = currentUser === null || currentUser === void 0 ? void 0 : currentUser.profilePic) !== null && _b !== void 0 ? _b : "/images/profile_placeholder.webp" }), _jsx(AvatarFallback, { children: "U" })] }), "Profile"] }) :
67
67
  _jsxs(Button, { variant: "ghost", onClick: () => router.push('/auth/login?role=user'), className: "w-full py-2 px-4 text-sm text-gray-700 hover:bg-gray-100 flex items-center gap-2 justify-start", children: [_jsxs(Avatar, { className: "w-4 h-4", children: [_jsx(AvatarImage, { src: "/images/profile_placeholder.webp" }), _jsx(AvatarFallback, { children: "U" })] }), "Login As Volunteer"] }), isLoggedIn && _jsxs(Link, { href: `/overview/${(currentUser === null || currentUser === void 0 ? void 0 : currentUser.role) || 'user'}`, className: "w-full py-2 px-4 text-sm text-gray-700 hover:bg-gray-100 flex items-center gap-2", children: [_jsx(LayoutDashboardIcon, { className: "h-4 w-4" }), "Dashboard"] }), isLoggedIn && ((currentUser === null || currentUser === void 0 ? void 0 : currentUser.role) === "user" || (currentUser === null || currentUser === void 0 ? void 0 : currentUser.role) == null) &&
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { zodResolver } from "@hookform/resolvers/zod";
4
- import { addReview, deleteReview, editReview, getReviewsById } from "@myrjfa/state/dist/lib/actions";
4
+ import { addReview, deleteReview, editReview, getReviewsById } from "@myrjfa/state/dist/lib/actions/actions";
5
5
  import { reviewBasicSchema } from "@myrjfa/state/dist/lib/models/review";
6
6
  import { userAtom, userLoadingAtom } from "@myrjfa/state/dist/lib/userAtom";
7
7
  import { formatDate } from "@myrjfa/state/dist/lib/utils";
@@ -0,0 +1,12 @@
1
+ type UploadType = "image" | "document" | "both";
2
+ interface FileUploadProps {
3
+ label?: string;
4
+ required?: boolean;
5
+ type?: UploadType;
6
+ value?: File | null;
7
+ onChange: (file: File | null) => void;
8
+ className?: string;
9
+ }
10
+ export declare function FileUpload({ label, required, type, value, onChange, className, }: FileUploadProps): import("react/jsx-runtime").JSX.Element;
11
+ export {};
12
+ //# sourceMappingURL=document-upload-button.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document-upload-button.d.ts","sourceRoot":"","sources":["../../src/misc/document-upload-button.tsx"],"names":[],"mappings":"AAMA,KAAK,UAAU,GAAG,OAAO,GAAG,UAAU,GAAG,MAAM,CAAC;AAEhD,UAAU,eAAe;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,KAAK,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACpB,QAAQ,EAAE,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,KAAK,IAAI,CAAC;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wBAAgB,UAAU,CAAC,EACvB,KAAqB,EACrB,QAAQ,EACR,IAAa,EACb,KAAK,EACL,QAAQ,EACR,SAAS,GACZ,EAAE,eAAe,2CAmEjB"}
@@ -0,0 +1,27 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useRef } from "react";
3
+ import { cn } from "@myrjfa/state/dist/lib/utils";
4
+ import { FileUp, ImageIcon, FileText } from "lucide-react";
5
+ import { Input } from "./input";
6
+ import { Label } from "./label";
7
+ export function FileUpload({ label = "Upload File", required, type = "both", value, onChange, className, }) {
8
+ const inputRef = useRef(null);
9
+ const acceptMap = {
10
+ image: "image/*",
11
+ document: "application/pdf",
12
+ both: "application/pdf,image/*",
13
+ };
14
+ const helperTextMap = {
15
+ image: "Image only",
16
+ document: "PDF only",
17
+ both: "PDF or image",
18
+ };
19
+ const Icon = type === "image"
20
+ ? ImageIcon
21
+ : type === "document"
22
+ ? FileText
23
+ : FileUp;
24
+ return (_jsxs("div", { className: cn("w-full", className), children: [_jsxs(Label, { className: "text-sm font-medium", children: [label, " ", required && _jsx("span", { className: "text-destructive", children: "*" }), _jsxs("span", { className: "text-xs text-muted-foreground ml-2", children: ["(", helperTextMap[type], ")"] })] }), _jsxs("div", { onClick: () => { var _a; return (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.click(); }, className: cn("mt-2 border-2 border-dashed rounded-lg cursor-pointer transition-all", "flex flex-col items-center justify-center text-center", "hover:border-primary/50",
25
+ // 📱 Responsive sizing
26
+ "p-3"), children: [_jsx(Icon, { className: "w-5 h-5 sm:w-6 sm:h-6 text-muted-foreground mb-1" }), value ? (_jsx("p", { className: "text-xs sm:text-sm text-primary font-medium break-all", children: value.name })) : (_jsx("p", { className: "text-xs sm:text-sm text-muted-foreground", children: "Tap or click to upload" })), _jsx(Input, { ref: inputRef, type: "file", accept: acceptMap[type], className: "hidden", onChange: (e) => { var _a, _b; return onChange((_b = (_a = e.target.files) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : null); } })] })] }));
27
+ }
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ interface EmptyStateProps {
3
+ title?: string;
4
+ description?: string;
5
+ icon?: React.ReactNode;
6
+ action?: React.ReactNode;
7
+ }
8
+ export declare function EmptyState({ title, description, icon, action, }: EmptyStateProps): import("react/jsx-runtime").JSX.Element;
9
+ export {};
10
+ //# sourceMappingURL=empty-state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"empty-state.d.ts","sourceRoot":"","sources":["../../src/misc/empty-state.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,UAAU,eAAe;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B;AAED,wBAAgB,UAAU,CAAC,EACvB,KAA0B,EAC1B,WAAW,EACX,IAAI,EACJ,MAAM,GACT,EAAE,eAAe,2CAajB"}
@@ -0,0 +1,5 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Inbox } from 'lucide-react';
3
+ export function EmptyState({ title = 'Nothing here yet', description, icon, action, }) {
4
+ return (_jsxs("div", { className: "flex flex-col items-center justify-center py-16 px-4 text-center gap-3", children: [_jsx("div", { className: "text-muted-foreground", children: icon !== null && icon !== void 0 ? icon : _jsx(Inbox, { className: "w-12 h-12 mx-auto mb-2 opacity-40" }) }), _jsx("h3", { className: "text-lg font-semibold text-foreground", children: title }), description && (_jsx("p", { className: "text-sm text-muted-foreground max-w-xs", children: description })), action && _jsx("div", { className: "mt-2", children: action })] }));
5
+ }
@@ -0,0 +1,29 @@
1
+ export interface CompressOptions {
2
+ quality?: number;
3
+ maxWidth?: number;
4
+ maxHeight?: number;
5
+ }
6
+ /**
7
+ * Compress an image File using Canvas API.
8
+ * Returns a new File object with the same name but compressed content.
9
+ */
10
+ export declare function compressImage(file: File, options?: CompressOptions): Promise<File>;
11
+ export interface ImageCompressorProps {
12
+ enableCompression?: boolean;
13
+ quality?: number;
14
+ maxWidth?: number;
15
+ maxHeight?: number;
16
+ accept?: string;
17
+ multiple?: boolean;
18
+ onChange: (files: File[]) => void;
19
+ children?: React.ReactNode;
20
+ className?: string;
21
+ id?: string;
22
+ }
23
+ /**
24
+ * #26 — Image Compressor Input Component
25
+ * Drop-in replacement for a file input that optionally compresses images
26
+ * before passing them to the onChange handler.
27
+ */
28
+ export declare function ImageCompressorInput({ enableCompression, quality, maxWidth, maxHeight, accept, multiple, onChange, children, className, id, }: ImageCompressorProps): import("react/jsx-runtime").JSX.Element;
29
+ //# sourceMappingURL=image-compressor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-compressor.d.ts","sourceRoot":"","sources":["../../src/misc/image-compressor.tsx"],"names":[],"mappings":"AAIA,MAAM,WAAW,eAAe;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;GAGG;AACH,wBAAsB,aAAa,CAC/B,IAAI,EAAE,IAAI,EACV,OAAO,GAAE,eAAoB,GAC9B,OAAO,CAAC,IAAI,CAAC,CAkDf;AAED,MAAM,WAAW,oBAAoB;IACjC,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC;IAClC,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,EAAE,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,EACjC,iBAAwB,EACxB,OAAa,EACb,QAAe,EACf,SAAgB,EAChB,MAAoD,EACpD,QAAgB,EAChB,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,EAAE,GACL,EAAE,oBAAoB,2CAmCtB"}
@@ -0,0 +1,70 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useRef, useCallback } from 'react';
4
+ /**
5
+ * Compress an image File using Canvas API.
6
+ * Returns a new File object with the same name but compressed content.
7
+ */
8
+ export async function compressImage(file, options = {}) {
9
+ const { quality = 0.8, maxWidth = 1920, maxHeight = 1920 } = options;
10
+ return new Promise((resolve, reject) => {
11
+ const img = new Image();
12
+ const url = URL.createObjectURL(file);
13
+ img.onload = () => {
14
+ URL.revokeObjectURL(url);
15
+ let { width, height } = img;
16
+ if (width > maxWidth) {
17
+ height = Math.round((height * maxWidth) / width);
18
+ width = maxWidth;
19
+ }
20
+ if (height > maxHeight) {
21
+ width = Math.round((width * maxHeight) / height);
22
+ height = maxHeight;
23
+ }
24
+ const canvas = document.createElement('canvas');
25
+ canvas.width = width;
26
+ canvas.height = height;
27
+ const ctx = canvas.getContext('2d');
28
+ if (!ctx) {
29
+ resolve(file); // fallback: return original
30
+ return;
31
+ }
32
+ ctx.drawImage(img, 0, 0, width, height);
33
+ canvas.toBlob((blob) => {
34
+ if (!blob) {
35
+ resolve(file); // fallback
36
+ return;
37
+ }
38
+ resolve(new File([blob], file.name, { type: 'image/webp', lastModified: Date.now() }));
39
+ }, 'image/webp', quality);
40
+ };
41
+ img.onerror = () => {
42
+ URL.revokeObjectURL(url);
43
+ reject(new Error(`Failed to load image: ${file.name}`));
44
+ };
45
+ img.src = url;
46
+ });
47
+ }
48
+ /**
49
+ * #26 — Image Compressor Input Component
50
+ * Drop-in replacement for a file input that optionally compresses images
51
+ * before passing them to the onChange handler.
52
+ */
53
+ export function ImageCompressorInput({ enableCompression = true, quality = 0.8, maxWidth = 1920, maxHeight = 1920, accept = 'image/jpeg,image/jpg,image/png,image/webp', multiple = false, onChange, children, className, id, }) {
54
+ const inputRef = useRef(null);
55
+ const handleChange = useCallback(async (e) => {
56
+ const rawFiles = Array.from(e.target.files || []);
57
+ if (!rawFiles.length)
58
+ return;
59
+ if (!enableCompression) {
60
+ onChange(rawFiles);
61
+ return;
62
+ }
63
+ const compressed = await Promise.all(rawFiles.map(f => compressImage(f, { quality, maxWidth, maxHeight }).catch(() => f)));
64
+ onChange(compressed);
65
+ // Reset input so the same file can be re-selected
66
+ if (inputRef.current)
67
+ inputRef.current.value = '';
68
+ }, [enableCompression, quality, maxWidth, maxHeight, onChange]);
69
+ return (_jsxs("label", { className: className, style: { cursor: 'pointer' }, children: [children, _jsx("input", { ref: inputRef, id: id, type: "file", accept: accept, multiple: multiple, onChange: handleChange, style: { display: 'none' } })] }));
70
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"input.d.ts","sourceRoot":"","sources":["../../src/misc/input.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAG9B,iBAAS,KAAK,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,2CAc1E;AAED,OAAO,EAAE,KAAK,EAAE,CAAA"}
1
+ {"version":3,"file":"input.d.ts","sourceRoot":"","sources":["../../src/misc/input.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAG9B,iBAAS,KAAK,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,2CAgB1E;AAED,OAAO,EAAE,KAAK,EAAE,CAAA"}
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { cn } from "@myrjfa/state/dist/lib/utils";
3
3
  function Input({ className, type, ...props }) {
4
- return (_jsx("input", { type: type, "data-slot": "input", className: cn("border-input file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm", "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]", "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", className), ...props }));
4
+ return (_jsx("input", { type: type, "data-slot": "input", onWheel: (e) => e.currentTarget.blur(), className: cn("border-input file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm", "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]", "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", "[&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none", className), ...props }));
5
5
  }
6
6
  export { Input };
@@ -3,7 +3,8 @@ type multiImageUploaderType = {
3
3
  handleImageUpload: (e: React.ChangeEvent<HTMLInputElement>) => Promise<void>;
4
4
  handleRemoveImage: (index: number) => void;
5
5
  message: string;
6
+ enableCompression?: boolean;
6
7
  };
7
- export default function MultiImageUploader({ previewImages, handleImageUpload, handleRemoveImage, message }: multiImageUploaderType): import("react/jsx-runtime").JSX.Element;
8
+ export default function MultiImageUploader({ previewImages, handleImageUpload, handleRemoveImage, message, enableCompression }: multiImageUploaderType): import("react/jsx-runtime").JSX.Element;
8
9
  export {};
9
10
  //# sourceMappingURL=multiImageUploader.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"multiImageUploader.d.ts","sourceRoot":"","sources":["../../src/misc/multiImageUploader.tsx"],"names":[],"mappings":"AAOA,KAAK,sBAAsB,GAAG;IAC1B,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,iBAAiB,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,WAAW,CAAC,gBAAgB,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7E,iBAAiB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,OAAO,EAAE,MAAM,CAAC;CACnB,CAAA;AAED,MAAM,CAAC,OAAO,UAAU,kBAAkB,CAAC,EAAE,aAAa,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,OAAO,EAAE,EAAE,sBAAsB,2CAuDlI"}
1
+ {"version":3,"file":"multiImageUploader.d.ts","sourceRoot":"","sources":["../../src/misc/multiImageUploader.tsx"],"names":[],"mappings":"AASA,KAAK,sBAAsB,GAAG;IAC1B,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,iBAAiB,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,WAAW,CAAC,gBAAgB,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7E,iBAAiB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC/B,CAAA;AAED,MAAM,CAAC,OAAO,UAAU,kBAAkB,CAAC,EAAE,aAAa,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,OAAO,EAAE,iBAAwB,EAAE,EAAE,sBAAsB,2CAgF5J"}
@@ -5,6 +5,30 @@ import { Input } from "./input";
5
5
  import { Card, CardHeader, CardTitle, CardContent } from "./card";
6
6
  import { Trash2, Upload } from "lucide-react";
7
7
  import Image from "next/image";
8
- export default function MultiImageUploader({ previewImages, handleImageUpload, handleRemoveImage, message }) {
9
- return (_jsxs(Card, { children: [_jsx(CardHeader, { children: _jsxs(CardTitle, { children: ["Images", _jsx(FormDescription, { children: message })] }) }), _jsxs(CardContent, { children: [_jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 mb-4", children: [previewImages.map((image, index) => (_jsxs("div", { className: "relative rounded-lg overflow-hidden h-40", children: [_jsx(Image, { id: "image-" + index, src: image, alt: `Opportunity image ${index + 1}`, fill: true, className: "object-cover" }), _jsx(Button, { type: "button", onClick: () => handleRemoveImage(index), className: "absolute top-2 right-2 bg-white/80 rounded-full hover:bg-white", children: _jsx(Trash2, { className: "text-red-500" }) })] }, index))), _jsxs("div", { className: "flex flex-col items-center justify-center w-full space-y-4", children: [_jsx("label", { htmlFor: "image", className: "cursor-pointer w-full", children: _jsxs("div", { className: "border-2 border-dashed border-slate-300 rounded-lg p-6 flex flex-col items-center justify-center text-slate-500 hover:border-primary hover:text-primary transition-colors", children: [_jsx(Upload, { className: "h-12 w-12 text-gray-500 mb-4" }), _jsx("span", { className: "text-lg font-semibold mb-2", children: "Upload Image" }), _jsx("span", { className: "text-sm", children: "Click or drag to upload" })] }) }), _jsx(Input, { id: "image", type: "file", accept: "image/*", multiple: true, onChange: handleImageUpload, placeholder: "Upload Image", className: "hidden" })] })] }), _jsx(FormDescription, { children: "Images should be in Valid format. Maximum file size: 5MB." })] })] }));
8
+ import { showAlert } from "./alertdialogclaude";
9
+ import { compressImage } from "./image-compressor";
10
+ export default function MultiImageUploader({ previewImages, handleImageUpload, handleRemoveImage, message, enableCompression = true }) {
11
+ return (_jsxs(Card, { children: [_jsx(CardHeader, { children: _jsxs(CardTitle, { children: ["Images", _jsx(FormDescription, { children: message })] }) }), _jsxs(CardContent, { children: [_jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 mb-4", children: [previewImages.map((image, index) => (_jsxs("div", { className: "relative rounded-lg overflow-hidden h-40", children: [_jsx(Image, { id: "image-" + index, src: image, alt: `Opportunity image ${index + 1}`, fill: true, className: "object-cover" }), _jsx(Button, { type: "button", onClick: () => handleRemoveImage(index), className: "absolute top-2 right-2 bg-white/80 rounded-full hover:bg-white", children: _jsx(Trash2, { className: "text-red-500" }) })] }, index))), _jsxs("div", { className: "flex flex-col items-center justify-center w-full space-y-4", children: [_jsx("label", { htmlFor: "image", className: "cursor-pointer w-full", children: _jsxs("div", { className: "border-2 border-dashed border-slate-300 rounded-lg p-6 flex flex-col items-center justify-center text-slate-500 hover:border-primary hover:text-primary transition-colors", children: [_jsx(Upload, { className: "h-12 w-12 text-gray-500 mb-4" }), _jsx("span", { className: "text-lg font-semibold mb-2", children: "Upload Image" }), _jsx("span", { className: "text-sm", children: "Click or drag to upload" })] }) }), _jsx(Input, { id: "image", type: "file", accept: "image/*", multiple: true, onChange: async (e) => {
12
+ const files = Array.from(e.target.files || []);
13
+ const allowedType = 'image/';
14
+ const invalid = files.filter(f => !f.type.includes(allowedType));
15
+ if (invalid.length > 0) {
16
+ showAlert(`Invalid file type(s): ${invalid.map(f => f.name).join(', ')}. Only Images are allowed.`);
17
+ e.target.value = '';
18
+ return;
19
+ }
20
+ if (enableCompression && files.length > 0) {
21
+ try {
22
+ const compressed = await Promise.all(files.map(f => compressImage(f)));
23
+ // Rebuild FileList via DataTransfer
24
+ const dt = new DataTransfer();
25
+ compressed.forEach(f => dt.items.add(f));
26
+ e.target.files = dt.files;
27
+ }
28
+ catch (_a) {
29
+ // compression failed — proceed with originals
30
+ }
31
+ }
32
+ handleImageUpload(e);
33
+ }, placeholder: "Upload Image", className: "hidden" })] })] }), _jsx(FormDescription, { children: "Images should be in Valid format. Maximum file size: 5MB." })] })] }));
10
34
  }
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { zodResolver } from "@hookform/resolvers/zod";
4
- import { subscribeToNewsLetter } from "@myrjfa/state/dist/lib/actions";
4
+ import { subscribeToNewsLetter } from "@myrjfa/state/dist/lib/actions/actions";
5
5
  import { usePathname } from "next/navigation";
6
6
  import { useEffect } from "react";
7
7
  import { useForm } from "react-hook-form";
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { Popover, PopoverContent, PopoverTrigger } from "@radix-ui/react-popover";
4
- import { getNotifications, toggleAllNotification, toggleNotification } from "@myrjfa/state/dist/lib/actions";
4
+ import { getNotifications, toggleAllNotification, toggleNotification } from "@myrjfa/state/dist/lib/actions/actions";
5
5
  import { formatTimestamp } from "@myrjfa/state/dist/lib/utils";
6
6
  import { Bell, MessageCircle, CheckCircle2, Receipt, Users, AlertCircle, Megaphone, Briefcase, ShieldCheck, ShieldX, Package, ClipboardList } from "lucide-react";
7
7
  import { useRouter } from "next/navigation";
@@ -0,0 +1,9 @@
1
+ /**
2
+ * #9 — Floating PWA Install Button
3
+ * Premium banner aligned with Trippeaze brand.
4
+ * - Only shows on mobile (beforeinstallprompt)
5
+ * - Auto-hides after 10 s
6
+ * - Animated entrance / exit
7
+ */
8
+ export declare function PwaInstallButton(): import("react/jsx-runtime").JSX.Element | null;
9
+ //# sourceMappingURL=pwa-install-button.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pwa-install-button.d.ts","sourceRoot":"","sources":["../../src/misc/pwa-install-button.tsx"],"names":[],"mappings":"AAKA;;;;;;GAMG;AACH,wBAAgB,gBAAgB,mDA0M/B"}
@@ -0,0 +1,158 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { useEffect, useRef, useState } from 'react';
4
+ import Image from 'next/image';
5
+ /**
6
+ * #9 — Floating PWA Install Button
7
+ * Premium banner aligned with Trippeaze brand.
8
+ * - Only shows on mobile (beforeinstallprompt)
9
+ * - Auto-hides after 10 s
10
+ * - Animated entrance / exit
11
+ */
12
+ export function PwaInstallButton() {
13
+ const [show, setShow] = useState(false);
14
+ const [hiding, setHiding] = useState(false);
15
+ const promptRef = useRef(null);
16
+ const timerRef = useRef(null);
17
+ const dismiss = () => {
18
+ setHiding(true);
19
+ setTimeout(() => setShow(false), 350);
20
+ };
21
+ useEffect(() => {
22
+ const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
23
+ if (!isMobile)
24
+ return;
25
+ const handler = (e) => {
26
+ e.preventDefault();
27
+ promptRef.current = e;
28
+ setShow(true);
29
+ setHiding(false);
30
+ timerRef.current = setTimeout(dismiss, 10000);
31
+ };
32
+ window.addEventListener('beforeinstallprompt', handler);
33
+ return () => {
34
+ window.removeEventListener('beforeinstallprompt', handler);
35
+ if (timerRef.current)
36
+ clearTimeout(timerRef.current);
37
+ };
38
+ }, []);
39
+ const handleInstall = async () => {
40
+ if (!promptRef.current)
41
+ return;
42
+ if (timerRef.current)
43
+ clearTimeout(timerRef.current);
44
+ promptRef.current.prompt();
45
+ const { outcome } = await promptRef.current.userChoice;
46
+ if (outcome === 'accepted')
47
+ dismiss();
48
+ };
49
+ if (!show)
50
+ return null;
51
+ return (_jsxs(_Fragment, { children: [_jsx("style", { children: `
52
+ @keyframes tpz-slide-up {
53
+ from { opacity: 0; transform: translateX(-50%) translateY(20px); }
54
+ to { opacity: 1; transform: translateX(-50%) translateY(0); }
55
+ }
56
+ @keyframes tpz-slide-down {
57
+ from { opacity: 1; transform: translateX(-50%) translateY(0); }
58
+ to { opacity: 0; transform: translateX(-50%) translateY(20px); }
59
+ }
60
+ @keyframes tpz-orbit {
61
+ from { transform: rotate(0deg) translateX(22px) rotate(0deg); }
62
+ to { transform: rotate(360deg) translateX(22px) rotate(-360deg); }
63
+ }
64
+ @keyframes tpz-shimmer {
65
+ 0% { background-position: -200% center; }
66
+ 100% { background-position: 200% center; }
67
+ }
68
+ ` }), _jsxs("div", { style: {
69
+ position: 'fixed',
70
+ bottom: '88px',
71
+ left: '50%',
72
+ transform: 'translateX(-50%)',
73
+ zIndex: 9999,
74
+ animation: hiding
75
+ ? 'tpz-slide-down 0.35s cubic-bezier(.4,0,.2,1) forwards'
76
+ : 'tpz-slide-up 0.4s cubic-bezier(.16,1,.3,1) forwards',
77
+ // Prevent layout shift during animation
78
+ willChange: 'transform, opacity',
79
+ }, children: [_jsx("div", { style: {
80
+ position: 'absolute',
81
+ inset: '-3px',
82
+ borderRadius: '9999px',
83
+ background: 'linear-gradient(135deg, #f97316, #fb923c, #22d3ee, #0891b2)',
84
+ opacity: 0.3,
85
+ filter: 'blur(8px)',
86
+ zIndex: 0,
87
+ } }), _jsxs("button", { onClick: handleInstall, style: {
88
+ position: 'relative',
89
+ zIndex: 1,
90
+ display: 'flex',
91
+ alignItems: 'center',
92
+ gap: '10px',
93
+ padding: '10px 18px 10px 12px',
94
+ borderRadius: '9999px',
95
+ border: '1px solid rgba(0,0,0,0.06)',
96
+ background: 'linear-gradient(135deg, #ffffff 0%, #f8fafc 100%)',
97
+ boxShadow: '0 12px 32px rgba(0,0,0,0.12), inset 0 1px 0 rgba(255,255,255,0.9)',
98
+ cursor: 'pointer',
99
+ whiteSpace: 'nowrap',
100
+ backdropFilter: 'blur(8px)',
101
+ }, children: [_jsx("div", { style: {
102
+ width: '36px',
103
+ height: '36px',
104
+ borderRadius: '50%',
105
+ overflow: 'hidden',
106
+ flexShrink: 0,
107
+ boxShadow: '0 2px 8px rgba(249,115,22,0.2)',
108
+ }, children: _jsx(Image, { src: "/images/logoicon.webp", alt: "Trippeaze", width: 36, height: 36, style: { objectFit: 'cover' } }) }), _jsxs("div", { style: { display: 'flex', flexDirection: 'column', alignItems: 'flex-start', gap: '1px' }, children: [_jsx("span", { style: {
109
+ fontSize: '11px',
110
+ fontWeight: 500,
111
+ letterSpacing: '0.08em',
112
+ textTransform: 'uppercase',
113
+ color: '#64748b',
114
+ lineHeight: 1,
115
+ }, children: "Get the app" }), _jsx("span", { style: {
116
+ fontSize: '15px',
117
+ fontWeight: 700,
118
+ letterSpacing: '-0.01em',
119
+ lineHeight: 1.2,
120
+ // Shimmer text using logo colors
121
+ background: 'linear-gradient(90deg, #fb923c, #fbbf24, #22d3ee, #fb923c)',
122
+ backgroundSize: '200% auto',
123
+ WebkitBackgroundClip: 'text',
124
+ WebkitTextFillColor: 'transparent',
125
+ backgroundClip: 'text',
126
+ animation: 'tpz-shimmer 3s linear infinite',
127
+ }, children: "Install Trippeaze" })] }), _jsx("div", { style: {
128
+ marginLeft: '4px',
129
+ width: '28px',
130
+ height: '28px',
131
+ borderRadius: '50%',
132
+ background: 'linear-gradient(135deg, #f97316, #dc2626)',
133
+ display: 'flex',
134
+ alignItems: 'center',
135
+ justifyContent: 'center',
136
+ flexShrink: 0,
137
+ boxShadow: '0 2px 8px rgba(249,115,22,0.5)',
138
+ }, children: _jsx("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", children: _jsx("path", { d: "M7 2v7M4 6.5l3 3 3-3M2.5 11h9", stroke: "white", strokeWidth: "1.7", strokeLinecap: "round", strokeLinejoin: "round" }) }) })] }), _jsx("button", { onClick: (e) => { e.stopPropagation(); dismiss(); }, "aria-label": "Dismiss", style: {
139
+ position: 'absolute',
140
+ top: '-6px',
141
+ right: '-6px',
142
+ zIndex: 2,
143
+ width: '20px',
144
+ height: '20px',
145
+ borderRadius: '50%',
146
+ background: '#ffffff',
147
+ border: '1px solid rgba(0,0,0,0.1)',
148
+ color: '#64748b',
149
+ fontSize: '11px',
150
+ lineHeight: '20px',
151
+ textAlign: 'center',
152
+ cursor: 'pointer',
153
+ display: 'flex',
154
+ alignItems: 'center',
155
+ justifyContent: 'center',
156
+ boxShadow: '0 2px 6px rgba(0,0,0,0.1)',
157
+ }, children: "\u2715" })] })] }));
158
+ }
@@ -1,3 +1,38 @@
1
+ import * as React from "react";
2
+ /**
3
+ * Base Skeleton component
4
+ * Centralized pulse animation and color.
5
+ */
1
6
  declare function Skeleton({ className, ...props }: React.ComponentProps<"div">): import("react/jsx-runtime").JSX.Element;
2
- export { Skeleton };
7
+ /** Horizontal Tile pattern */
8
+ declare function SkeletonTile({ className }: {
9
+ className?: string;
10
+ }): import("react/jsx-runtime").JSX.Element;
11
+ /** Vertical List pattern */
12
+ declare function SkeletonList({ count, className }: {
13
+ count?: number;
14
+ className?: string;
15
+ }): import("react/jsx-runtime").JSX.Element;
16
+ /**
17
+ * Full-page loading skeleton (Default Export)
18
+ * Displayed while data is loading.
19
+ */
20
+ export default function LoadingSkeleton({ count }: {
21
+ count?: number;
22
+ }): import("react/jsx-runtime").JSX.Element;
23
+ /** Card skeleton for feed-style layouts */
24
+ declare function CardSkeleton({ className }: {
25
+ className?: string;
26
+ }): import("react/jsx-runtime").JSX.Element;
27
+ /** Feed grid skeleton (6 cards by default) */
28
+ declare function FeedSkeleton({ count }: {
29
+ count?: number;
30
+ }): import("react/jsx-runtime").JSX.Element;
31
+ /** Page-level skeleton for detail pages (opportunity/package/blog) */
32
+ declare function DetailPageSkeleton(): import("react/jsx-runtime").JSX.Element;
33
+ /** Form field skeleton */
34
+ declare function FormSkeleton({ fields }: {
35
+ fields?: number;
36
+ }): import("react/jsx-runtime").JSX.Element;
37
+ export { Skeleton, CardSkeleton, SkeletonTile, SkeletonList, DetailPageSkeleton, FormSkeleton, FeedSkeleton };
3
38
  //# sourceMappingURL=skeleton.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"skeleton.d.ts","sourceRoot":"","sources":["../../src/misc/skeleton.tsx"],"names":[],"mappings":"AAEA,iBAAS,QAAQ,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,2CAQrE;AAED,OAAO,EAAE,QAAQ,EAAE,CAAA"}
1
+ {"version":3,"file":"skeleton.d.ts","sourceRoot":"","sources":["../../src/misc/skeleton.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B;;;GAGG;AACH,iBAAS,QAAQ,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,2CAQrE;AAED,8BAA8B;AAC9B,iBAAS,YAAY,CAAC,EAAE,SAAS,EAAE,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,2CAY1D;AAED,4BAA4B;AAC5B,iBAAS,YAAY,CAAC,EAAE,KAAS,EAAE,SAAS,EAAE,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,2CAQrF;AAED;;;GAGG;AACH,MAAM,CAAC,OAAO,UAAU,eAAe,CAAC,EAAE,KAAS,EAAE,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,2CAiBxE;AAED,2CAA2C;AAC3C,iBAAS,YAAY,CAAC,EAAE,SAAc,EAAE,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,2CAe/D;AAED,8CAA8C;AAC9C,iBAAS,YAAY,CAAC,EAAE,KAAS,EAAE,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,2CAQtD;AAED,sEAAsE;AACtE,iBAAS,kBAAkB,4CA8C1B;AAED,0BAA0B;AAC1B,iBAAS,YAAY,CAAC,EAAE,MAAU,EAAE,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,2CAYxD;AAGD,OAAO,EACL,QAAQ,EACR,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,kBAAkB,EAClB,YAAY,EACZ,YAAY,EACb,CAAA"}
@@ -1,6 +1,43 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
3
  import { cn } from "@myrjfa/state/dist/lib/utils";
4
+ /**
5
+ * Base Skeleton component
6
+ * Centralized pulse animation and color.
7
+ */
3
8
  function Skeleton({ className, ...props }) {
4
9
  return (_jsx("div", { "data-slot": "skeleton", className: cn("bg-primary/10 animate-pulse rounded-md", className), ...props }));
5
10
  }
6
- export { Skeleton };
11
+ /** Horizontal Tile pattern */
12
+ function SkeletonTile({ className }) {
13
+ return (_jsxs("div", { className: cn("flex gap-3 items-start p-3 rounded-lg border border-border", className), children: [_jsx(Skeleton, { className: "h-20 w-20 rounded-lg shrink-0" }), _jsxs("div", { className: "flex-1 space-y-2", children: [_jsx(Skeleton, { className: "h-4 w-2/3" }), _jsx(Skeleton, { className: "h-3 w-full" }), _jsx(Skeleton, { className: "h-3 w-4/5" }), _jsx(Skeleton, { className: "h-5 w-24 rounded-full" })] })] }));
14
+ }
15
+ /** Vertical List pattern */
16
+ function SkeletonList({ count = 4, className }) {
17
+ return (_jsx("div", { className: cn("space-y-3", className), children: Array.from({ length: count }).map((_, i) => (_jsx(SkeletonTile, {}, i))) }));
18
+ }
19
+ /**
20
+ * Full-page loading skeleton (Default Export)
21
+ * Displayed while data is loading.
22
+ */
23
+ export default function LoadingSkeleton({ count = 4 }) {
24
+ return (_jsx("div", { className: "w-full h-screen flex items-center justify-center", children: _jsx("div", { className: "w-full max-w-2xl p-6 space-y-4", children: Array.from({ length: count }).map((_, i) => (_jsxs("div", { className: "animate-pulse space-y-3", children: [_jsx("div", { className: "h-4 bg-muted rounded w-3/4" }), _jsx("div", { className: "h-4 bg-muted rounded w-1/2" }), _jsx("div", { className: "h-3 bg-muted rounded w-full" })] }, i))) }) }));
25
+ }
26
+ /** Card skeleton for feed-style layouts */
27
+ function CardSkeleton({ className = '' }) {
28
+ return (_jsxs("div", { className: `rounded-xl overflow-hidden border border-border animate-pulse ${className}`, children: [_jsx("div", { className: "h-48 bg-muted w-full" }), _jsxs("div", { className: "p-4 space-y-3", children: [_jsx("div", { className: "h-4 bg-muted rounded w-3/4" }), _jsx("div", { className: "h-3 bg-muted rounded w-full" }), _jsx("div", { className: "h-3 bg-muted rounded w-5/6" }), _jsxs("div", { className: "flex gap-2 pt-2", children: [_jsx("div", { className: "h-6 bg-muted rounded-full w-16" }), _jsx("div", { className: "h-6 bg-muted rounded-full w-16" })] })] })] }));
29
+ }
30
+ /** Feed grid skeleton (6 cards by default) */
31
+ function FeedSkeleton({ count = 6 }) {
32
+ return (_jsx("div", { className: "w-full p-6 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4", children: Array.from({ length: count }).map((_, i) => (_jsx(CardSkeleton, {}, i))) }));
33
+ }
34
+ /** Page-level skeleton for detail pages (opportunity/package/blog) */
35
+ function DetailPageSkeleton() {
36
+ return (_jsx("div", { className: "min-h-screen bg-slate-50 mt-15", children: _jsxs("div", { className: "container mx-auto px-4 py-6 flex flex-col lg:flex-row gap-10", children: [_jsx("div", { className: "lg:w-80 shrink-0", children: _jsxs("div", { className: "bg-white rounded-lg shadow-sm p-4 animate-pulse space-y-3", children: [_jsx("div", { className: "h-5 bg-muted rounded w-1/2" }), [1, 2, 3, 4, 5].map(i => (_jsx("div", { className: "h-4 bg-muted rounded w-3/4" }, i)))] }) }), _jsxs("main", { className: "flex-1 lg:pr-5 flex flex-col gap-6", children: [_jsxs("div", { className: "bg-white rounded-lg shadow-sm overflow-hidden animate-pulse", children: [_jsx("div", { className: "h-72 bg-muted" }), _jsxs("div", { className: "p-6 space-y-3", children: [_jsx("div", { className: "h-6 bg-muted rounded w-2/3" }), _jsx("div", { className: "h-4 bg-muted rounded w-1/2" })] })] }), _jsxs("div", { className: "bg-white rounded-lg shadow-sm p-6 animate-pulse space-y-4", children: [_jsx("div", { className: "h-5 bg-muted rounded w-1/4" }), _jsx("div", { className: "grid grid-cols-1 md:grid-cols-3 gap-4", children: [1, 2, 3].map(i => (_jsxs("div", { className: "space-y-2", children: [_jsx("div", { className: "h-4 bg-muted rounded w-1/2" }), _jsx("div", { className: "h-3 bg-muted rounded w-3/4" })] }, i))) })] }), _jsxs("div", { className: "bg-white rounded-lg shadow-sm p-6 animate-pulse space-y-3", children: [_jsx("div", { className: "h-5 bg-muted rounded w-1/4" }), [1, 2, 3, 4].map(i => (_jsx("div", { className: "h-3 bg-muted rounded", style: { width: `${60 + Math.random() * 40}%` } }, i)))] })] })] }) }));
37
+ }
38
+ /** Form field skeleton */
39
+ function FormSkeleton({ fields = 4 }) {
40
+ return (_jsxs("div", { className: "space-y-6", children: [Array.from({ length: fields }).map((_, i) => (_jsxs("div", { className: "space-y-2", children: [_jsx(Skeleton, { className: "h-4 w-1/4" }), _jsx(Skeleton, { className: "h-10 w-full" })] }, i))), _jsx(Skeleton, { className: "h-12 w-full" })] }));
41
+ }
42
+ // Aliases for backward compatibility or different naming preferences
43
+ export { Skeleton, CardSkeleton, SkeletonTile, SkeletonList, DetailPageSkeleton, FormSkeleton, FeedSkeleton };
package/dist/misc/tile.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { toggleUserWishlist } from "@myrjfa/state/dist/lib/actions";
3
+ import { toggleUserWishlist } from "@myrjfa/state/dist/lib/actions/actions";
4
4
  import { userAtom, userLoadingAtom } from "@myrjfa/state/dist/lib/userAtom";
5
5
  import { cn, formatDate, truncateText } from "@myrjfa/state/dist/lib/utils";
6
6
  import { useAtom, useAtomValue } from "jotai";
@@ -0,0 +1,9 @@
1
+ /**
2
+ * #21 — Floating WhatsApp Help Button
3
+ * Fixed bottom-right pill button that opens a WhatsApp chat with support.
4
+ * Matches the "Need help? Chat with us" pill + green icon design.
5
+ */
6
+ export declare function WhatsAppHelpButton({ message }: {
7
+ message: string;
8
+ }): import("react/jsx-runtime").JSX.Element;
9
+ //# sourceMappingURL=whatsapp-help.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"whatsapp-help.d.ts","sourceRoot":"","sources":["../../src/misc/whatsapp-help.tsx"],"names":[],"mappings":"AAKA;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,OAAO,EAAE,EAAE;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,2CAyClE"}
@@ -0,0 +1,13 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { WhatsappIcon } from 'react-share';
4
+ import Link from 'next/link';
5
+ /**
6
+ * #21 — Floating WhatsApp Help Button
7
+ * Fixed bottom-right pill button that opens a WhatsApp chat with support.
8
+ * Matches the "Need help? Chat with us" pill + green icon design.
9
+ */
10
+ export function WhatsAppHelpButton({ message }) {
11
+ const whatsappUrl = `https://wa.me/918482944029?text=${encodeURIComponent(message)}`;
12
+ return (_jsxs(Link, { href: whatsappUrl, target: "_blank", rel: "noopener noreferrer", "aria-label": "Get help on WhatsApp", className: "\r\n fixed bottom-6 right-6 z-[9998]\r\n flex items-center gap-0\r\n rounded-full\r\n bg-white\r\n shadow-[0_2px_12px_rgba(0,0,0,0.15)]\r\n hover:shadow-[0_4px_16px_rgba(0,0,0,0.2)]\r\n border border-gray-200\r\n transition-all duration-200\r\n hover:scale-[1.03]\r\n overflow-hidden\r\n no-underline\r\n group\r\n ", children: [_jsx("span", { className: "\r\n pl-4 pr-3 py-2.5\r\n text-sm font-medium text-gray-700\r\n whitespace-nowrap\r\n group-hover:text-gray-900\r\n transition-colors\r\n ", children: "Need help? Chat with us" }), _jsx("span", { className: "flex-shrink-0 pr-0.5 py-0.5", children: _jsx(WhatsappIcon, { size: 42, round: true }) })] }));
13
+ }
package/package.json CHANGED
@@ -1,9 +1,11 @@
1
1
  {
2
2
  "name": "@myrjfa/ui",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
+ "type": "module",
4
5
  "description": "Shared UI components for my apps",
5
6
  "main": "dist/index.js",
6
7
  "types": "dist/index.d.ts",
8
+ "sideEffects": false,
7
9
  "files": [
8
10
  "dist"
9
11
  ],
@@ -36,7 +38,6 @@
36
38
  "cmdk": "1.1.1",
37
39
  "embla-carousel-react": "^8.5.2",
38
40
  "emoji-picker-react": "^4.18.0",
39
- "pnpm": "^10.14.0",
40
41
  "react-easy-crop": "^5.5.6",
41
42
  "react-hook-form": "^7.54.2",
42
43
  "react-icons": "^5.5.0",
@@ -49,11 +50,11 @@
49
50
  "lucide-react": "*",
50
51
  "next": "*",
51
52
  "react": "*",
52
- "react-dom": "",
53
+ "react-dom": "*",
53
54
  "zod": "*"
54
55
  },
55
56
  "devDependencies": {
56
- "@myrjfa/state": "1.1.1",
57
+ "@myrjfa/state": "1.1.2",
57
58
  "@types/react": "19.0.10",
58
59
  "@types/react-dom": "^19.0.4",
59
60
  "date-fns": "^3.0.0",
@@ -1,10 +0,0 @@
1
- interface UserProfileDialogProps {
2
- userId: string;
3
- userName: string;
4
- userImage?: string;
5
- open: boolean;
6
- onClose: () => void;
7
- }
8
- export declare function UserProfileDialog({ userId, userName, userImage, open, onClose }: UserProfileDialogProps): import("react/jsx-runtime").JSX.Element;
9
- export {};
10
- //# sourceMappingURL=UserProfileDialog.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"UserProfileDialog.d.ts","sourceRoot":"","sources":["../../../src/chat/dialogs/UserProfileDialog.tsx"],"names":[],"mappings":"AASA,UAAU,sBAAsB;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;CACvB;AAED,wBAAgB,iBAAiB,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,sBAAsB,2CAkFvG"}
@@ -1,100 +0,0 @@
1
- "use client";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
- var __generator = (this && this.__generator) || function (thisArg, body) {
12
- var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
13
- return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
- function verb(n) { return function (v) { return step([n, v]); }; }
15
- function step(op) {
16
- if (f) throw new TypeError("Generator is already executing.");
17
- while (g && (g = 0, op[0] && (_ = 0)), _) try {
18
- if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19
- if (y = 0, t) op = [op[0] & 2, t.value];
20
- switch (op[0]) {
21
- case 0: case 1: t = op; break;
22
- case 4: _.label++; return { value: op[1], done: false };
23
- case 5: _.label++; y = op[1]; op = [0]; continue;
24
- case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
- default:
26
- if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
- if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
- if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
- if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
- if (t[2]) _.ops.pop();
31
- _.trys.pop(); continue;
32
- }
33
- op = body.call(thisArg, _);
34
- } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
- if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
- }
37
- };
38
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
39
- import { useState, useEffect } from "react";
40
- import { Dialog, DialogContent, DialogHeader, DialogTitle } from "../../misc/dialog";
41
- import { Button } from "../../misc/button";
42
- import { Avatar, AvatarFallback, AvatarImage } from "../../misc/avatar";
43
- import { Ban, Shield } from "lucide-react";
44
- import { userApi } from "@myrjfa/state/dist/lib/actions/user";
45
- export function UserProfileDialog(_a) {
46
- var _this = this;
47
- var userId = _a.userId, userName = _a.userName, userImage = _a.userImage, open = _a.open, onClose = _a.onClose;
48
- var _b = useState(false), isBlocked = _b[0], setIsBlocked = _b[1];
49
- var _c = useState(false), loading = _c[0], setLoading = _c[1];
50
- useEffect(function () {
51
- if (open && userId) {
52
- checkBlockStatus();
53
- }
54
- }, [open, userId]);
55
- var checkBlockStatus = function () { return __awaiter(_this, void 0, void 0, function () {
56
- var blocked;
57
- return __generator(this, function (_a) {
58
- switch (_a.label) {
59
- case 0: return [4 /*yield*/, userApi.checkIfBlocked(userId)];
60
- case 1:
61
- blocked = _a.sent();
62
- setIsBlocked(blocked);
63
- return [2 /*return*/];
64
- }
65
- });
66
- }); };
67
- var handleBlockToggle = function () { return __awaiter(_this, void 0, void 0, function () {
68
- var error_1;
69
- return __generator(this, function (_a) {
70
- switch (_a.label) {
71
- case 0:
72
- setLoading(true);
73
- _a.label = 1;
74
- case 1:
75
- _a.trys.push([1, 6, 7, 8]);
76
- if (!isBlocked) return [3 /*break*/, 3];
77
- return [4 /*yield*/, userApi.unblockUser(userId)];
78
- case 2:
79
- _a.sent();
80
- setIsBlocked(false);
81
- return [3 /*break*/, 5];
82
- case 3: return [4 /*yield*/, userApi.blockUser(userId)];
83
- case 4:
84
- _a.sent();
85
- setIsBlocked(true);
86
- _a.label = 5;
87
- case 5: return [3 /*break*/, 8];
88
- case 6:
89
- error_1 = _a.sent();
90
- console.error("Failed to toggle block:", error_1);
91
- return [3 /*break*/, 8];
92
- case 7:
93
- setLoading(false);
94
- return [7 /*endfinally*/];
95
- case 8: return [2 /*return*/];
96
- }
97
- });
98
- }); };
99
- return (_jsx(Dialog, { open: open, onOpenChange: onClose, children: _jsxs(DialogContent, { className: "sm:max-w-md", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: "User Profile" }) }), _jsxs("div", { className: "flex flex-col items-center space-y-4 py-4", children: [_jsxs(Avatar, { className: "h-24 w-24", children: [_jsx(AvatarImage, { src: userImage }), _jsx(AvatarFallback, { className: "text-2xl", children: userName.charAt(0).toUpperCase() })] }), _jsxs("div", { className: "text-center", children: [_jsx("h3", { className: "text-lg font-semibold", children: userName }), _jsxs("p", { className: "text-sm text-gray-500", children: ["@", userId] })] }), _jsx(Button, { onClick: handleBlockToggle, disabled: loading, variant: isBlocked ? "outline" : "destructive", className: "w-full", children: loading ? ("Processing...") : isBlocked ? (_jsxs(_Fragment, { children: [_jsx(Shield, { className: "w-4 h-4 mr-2" }), "Unblock User"] })) : (_jsxs(_Fragment, { children: [_jsx(Ban, { className: "w-4 h-4 mr-2" }), "Block User"] })) }), isBlocked && (_jsx("p", { className: "text-xs text-gray-500 text-center", children: "You have blocked this user. They cannot start chats with you." }))] })] }) }));
100
- }
@@ -1,2 +0,0 @@
1
- export default function PortfolioPage(): import("react/jsx-runtime").JSX.Element;
2
- //# sourceMappingURL=temp-page.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"temp-page.d.ts","sourceRoot":"","sources":["../../../src/main/portfolio/temp-page.tsx"],"names":[],"mappings":"AAYA,MAAM,CAAC,OAAO,UAAU,aAAa,4CAmKpC"}
@@ -1,102 +0,0 @@
1
- // app/(dashboard)/profile/[userId]/portfolio/page.tsx
2
- 'use client';
3
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
4
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
5
- return new (P || (P = Promise))(function (resolve, reject) {
6
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
7
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
8
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
9
- step((generator = generator.apply(thisArg, _arguments || [])).next());
10
- });
11
- };
12
- var __generator = (this && this.__generator) || function (thisArg, body) {
13
- var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
14
- return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
15
- function verb(n) { return function (v) { return step([n, v]); }; }
16
- function step(op) {
17
- if (f) throw new TypeError("Generator is already executing.");
18
- while (g && (g = 0, op[0] && (_ = 0)), _) try {
19
- if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
20
- if (y = 0, t) op = [op[0] & 2, t.value];
21
- switch (op[0]) {
22
- case 0: case 1: t = op; break;
23
- case 4: _.label++; return { value: op[1], done: false };
24
- case 5: _.label++; y = op[1]; op = [0]; continue;
25
- case 7: op = _.ops.pop(); _.trys.pop(); continue;
26
- default:
27
- if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
28
- if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
29
- if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
30
- if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
31
- if (t[2]) _.ops.pop();
32
- _.trys.pop(); continue;
33
- }
34
- op = body.call(thisArg, _);
35
- } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
36
- if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
37
- }
38
- };
39
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
40
- import { useEffect, useState, useRef } from 'react';
41
- import { useParams, useRouter, useSearchParams } from 'next/navigation';
42
- import Image from 'next/image';
43
- import { Button } from '@myrjfa/ui/dist/misc/button';
44
- import { ExternalLink, ChevronLeft } from 'lucide-react';
45
- import ShareButton from '@myrjfa/ui/dist/misc/share';
46
- import { getUserPortfolio } from '@/lib/actions';
47
- export default function PortfolioPage() {
48
- var _this = this;
49
- var router = useRouter();
50
- var id = useParams().id;
51
- var userId = decodeURIComponent(id);
52
- var searchParams = useSearchParams();
53
- var firstName = searchParams.get("firstName");
54
- var _a = useState([]), portfolio = _a[0], setPortfolio = _a[1];
55
- var _b = useState(0), currentIndex = _b[0], setCurrentIndex = _b[1];
56
- var containerRef = useRef(null);
57
- useEffect(function () {
58
- // Fetch portfolio data
59
- // This should match your existing data fetching logic
60
- var fetchData = function () { return __awaiter(_this, void 0, void 0, function () {
61
- var portfolioResponse;
62
- return __generator(this, function (_a) {
63
- switch (_a.label) {
64
- case 0: return [4 /*yield*/, getUserPortfolio(userId)];
65
- case 1:
66
- portfolioResponse = _a.sent();
67
- if (portfolioResponse.error)
68
- console.error(portfolioResponse.error);
69
- else
70
- setPortfolio(portfolioResponse.portfolios);
71
- return [2 /*return*/];
72
- }
73
- });
74
- }); };
75
- fetchData();
76
- var index = searchParams.get('index');
77
- if (index) {
78
- setCurrentIndex(parseInt(index));
79
- }
80
- }, [userId, searchParams]);
81
- useEffect(function () {
82
- // Scroll to current item
83
- if (containerRef.current) {
84
- var items = containerRef.current.children;
85
- if (items[currentIndex]) {
86
- items[currentIndex].scrollIntoView({ behavior: 'smooth', block: 'start' });
87
- }
88
- }
89
- }, [currentIndex]);
90
- var handleScroll = function () {
91
- if (!containerRef.current)
92
- return;
93
- var container = containerRef.current;
94
- var scrollPosition = container.scrollTop;
95
- var itemHeight = window.innerHeight;
96
- var newIndex = Math.round(scrollPosition / itemHeight);
97
- if (newIndex !== currentIndex && newIndex >= 0 && newIndex < portfolio.length) {
98
- setCurrentIndex(newIndex);
99
- }
100
- };
101
- return (_jsxs("div", { className: "fixed inset-0 bg-black z-50 mt-15", children: [_jsx("div", { className: "absolute top-0 left-0 right-0 z-10 bg-gradient-to-b from-black/50 to-transparent", children: _jsxs("div", { className: "flex items-center justify-between p-4", children: [_jsx(Button, { variant: "ghost", size: "sm", onClick: function () { return router.back(); }, className: "text-white hover:bg-white/10", children: _jsx(ChevronLeft, { className: "h-6 w-6" }) }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "w-8 h-8 bg-gradient-to-r from-blue-500 to-purple-500 rounded-full flex items-center justify-center", children: _jsx("span", { className: "text-white text-xs font-bold", children: "P" }) }), _jsxs("span", { className: "text-white font-semibold", children: [firstName, "'s Portfolio"] })] }), _jsx("div", { className: "w-10" }), " "] }) }), _jsx("div", { ref: containerRef, onScroll: handleScroll, className: "h-full overflow-y-scroll snap-y snap-mandatory scrollbar-hide", style: { scrollbarWidth: 'none', msOverflowStyle: 'none' }, children: portfolio.map(function (item, index) { return (_jsxs("div", { className: "h-screen w-full snap-start snap-always relative flex flex-col", children: [_jsx("div", { className: "flex-1 flex items-center justify-center bg-black", children: item.mediaType === 'video' ? (_jsx("video", { src: item.url, controls: true, loop: true, playsInline: true, className: "max-w-full max-h-full object-contain" })) : (_jsx("div", { className: "relative w-full h-full", children: _jsx(Image, { src: item.url, alt: item.caption, fill: true, sizes: "100vw", className: "object-contain", priority: index === currentIndex }) })) }), _jsx("div", { className: "absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/80 via-black/50 to-transparent p-4 pb-6", children: _jsxs("div", { className: "space-y-3", children: [_jsxs("div", { className: "flex items-center gap-4", children: [_jsx(ShareButton, {}), item.mainUrl && (_jsx(Button, { variant: "ghost", size: "sm", onClick: function () { return window.open(item.mainUrl, '_blank'); }, className: "text-white hover:bg-white/10", children: _jsx(ExternalLink, { className: "w-5 h-5" }) }))] }), _jsxs("div", { className: "text-white text-sm", children: [_jsx("p", { className: "font-medium", children: "5 likes" }), _jsxs("p", { className: "text-white/70 text-xs", children: [item.comments || 0, " comments \u2022 ", item.shares || 0, " shares"] })] }), _jsx("div", { className: "text-white", children: _jsxs("p", { className: "text-sm leading-relaxed line-clamp-3", children: [_jsx("span", { className: "font-semibold", children: firstName }), " ", item.caption] }) }), _jsx("div", { className: "flex gap-1 justify-center pt-2", children: portfolio.map(function (_, i) { return (_jsx("div", { className: "h-0.5 flex-1 rounded-full transition-all ".concat(i === currentIndex ? 'bg-white' : 'bg-white/30') }, i)); }) })] }) })] }, item._id)); }) }), _jsx("style", { jsx: true, children: "\n .scrollbar-hide::-webkit-scrollbar {\n display: none;\n }\n " })] }));
102
- }