@myrjfa/ui 2.0.1 → 2.1.1

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 (67) hide show
  1. package/LICENSE +201 -201
  2. package/README.md +2 -2
  3. package/dist/chat/ChatMain.d.ts +3 -1
  4. package/dist/chat/ChatMain.d.ts.map +1 -1
  5. package/dist/chat/ChatMain.js +2 -1
  6. package/dist/chat/MessageBubble.js +4 -4
  7. package/dist/chat/MessageInput.d.ts +1 -1
  8. package/dist/chat/MessageInput.d.ts.map +1 -1
  9. package/dist/chat/components/ParticipantSelector.d.ts +1 -1
  10. package/dist/chat/components/ParticipantSelector.d.ts.map +1 -1
  11. package/dist/dashboard/app-sidebar.js +3 -3
  12. package/dist/dashboard/data-table.d.ts +2 -1
  13. package/dist/dashboard/data-table.d.ts.map +1 -1
  14. package/dist/dashboard/data-table.js +3 -3
  15. package/dist/index.d.ts +6 -0
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +7 -1
  18. package/dist/main/navbar.js +5 -5
  19. package/dist/main/review-page.d.ts +1 -1
  20. package/dist/main/review-page.d.ts.map +1 -1
  21. package/dist/main/review-page.js +49 -9
  22. package/dist/misc/alertdialogclaude.d.ts.map +1 -1
  23. package/dist/misc/alertdialogclaude.js +1 -1
  24. package/dist/misc/command.js +1 -1
  25. package/dist/misc/dashboard/action-buttons.js +31 -0
  26. package/dist/misc/document-upload-button.d.ts +12 -0
  27. package/dist/misc/document-upload-button.d.ts.map +1 -0
  28. package/dist/misc/document-upload-button.js +27 -0
  29. package/dist/misc/empty-state.d.ts +10 -0
  30. package/dist/misc/empty-state.d.ts.map +1 -0
  31. package/dist/misc/empty-state.js +5 -0
  32. package/dist/misc/filter.d.ts.map +1 -1
  33. package/dist/misc/filter.js +1 -1
  34. package/dist/misc/image-compressor.d.ts +29 -0
  35. package/dist/misc/image-compressor.d.ts.map +1 -0
  36. package/dist/misc/image-compressor.js +70 -0
  37. package/dist/misc/image-cropper.d.ts +10 -0
  38. package/dist/misc/image-cropper.d.ts.map +1 -0
  39. package/dist/misc/image-cropper.js +44 -0
  40. package/dist/misc/input.d.ts.map +1 -1
  41. package/dist/misc/input.js +1 -1
  42. package/dist/misc/misc/alertdialogclaude.js +221 -0
  43. package/dist/misc/misc/button.js +46 -0
  44. package/dist/misc/misc/dropdown-menu.js +136 -0
  45. package/dist/misc/multiImageUploader.d.ts +2 -1
  46. package/dist/misc/multiImageUploader.d.ts.map +1 -1
  47. package/dist/misc/multiImageUploader.js +26 -2
  48. package/dist/misc/newsLetter.js +1 -1
  49. package/dist/misc/notification.js +2 -2
  50. package/dist/misc/popover.d.ts.map +1 -1
  51. package/dist/misc/popover.js +1 -1
  52. package/dist/misc/pwa-install-button.d.ts +9 -0
  53. package/dist/misc/pwa-install-button.d.ts.map +1 -0
  54. package/dist/misc/pwa-install-button.js +158 -0
  55. package/dist/misc/select.d.ts.map +1 -1
  56. package/dist/misc/select.js +3 -3
  57. package/dist/misc/skeleton.d.ts +36 -1
  58. package/dist/misc/skeleton.d.ts.map +1 -1
  59. package/dist/misc/skeleton.js +40 -3
  60. package/dist/misc/tile.js +3 -3
  61. package/dist/misc/whatsapp-help.d.ts +9 -0
  62. package/dist/misc/whatsapp-help.d.ts.map +1 -0
  63. package/dist/misc/whatsapp-help.js +13 -0
  64. package/dist/profile/socialLink.d.ts +1 -1
  65. package/dist/profile/socialLink.d.ts.map +1 -1
  66. package/dist/profile/socialLink.js +49 -19
  67. package/package.json +5 -4
@@ -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 +1 @@
1
- {"version":3,"file":"select.d.ts","sourceRoot":"","sources":["../../src/misc/select.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,eAAe,MAAM,wBAAwB,CAAA;AAKzD,iBAAS,MAAM,CAAC,EACd,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,IAAI,CAAC,2CAEnD;AAED,iBAAS,WAAW,CAAC,EACnB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,KAAK,CAAC,2CAEpD;AAED,iBAAS,WAAW,CAAC,EACnB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,KAAK,CAAC,2CAEpD;AAED,iBAAS,aAAa,CAAC,EACrB,SAAS,EACT,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,OAAO,CAAC,2CAgBtD;AAED,iBAAS,aAAa,CAAC,EACrB,SAAS,EACT,QAAQ,EACR,QAAmB,EACnB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,OAAO,CAAC,2CA4BtD;AAED,iBAAS,WAAW,CAAC,EACnB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,KAAK,CAAC,2CAQpD;AAED,iBAAS,UAAU,CAAC,EAClB,SAAS,EACT,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,IAAI,CAAC,2CAkBnD;AAED,iBAAS,eAAe,CAAC,EACvB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,SAAS,CAAC,2CAQxD;AAED,iBAAS,oBAAoB,CAAC,EAC5B,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,cAAc,CAAC,2CAa7D;AAED,iBAAS,sBAAsB,CAAC,EAC9B,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,gBAAgB,CAAC,2CAa/D;AAED,OAAO,EACL,MAAM,EACN,aAAa,EACb,WAAW,EACX,UAAU,EACV,WAAW,EACX,sBAAsB,EACtB,oBAAoB,EACpB,eAAe,EACf,aAAa,EACb,WAAW,GACZ,CAAA"}
1
+ {"version":3,"file":"select.d.ts","sourceRoot":"","sources":["../../src/misc/select.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,eAAe,MAAM,wBAAwB,CAAA;AAKzD,iBAAS,MAAM,CAAC,EACd,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,IAAI,CAAC,2CAEnD;AAED,iBAAS,WAAW,CAAC,EACnB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,KAAK,CAAC,2CAEpD;AAED,iBAAS,WAAW,CAAC,EACnB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,KAAK,CAAC,2CAEpD;AAED,iBAAS,aAAa,CAAC,EACrB,SAAS,EACT,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,OAAO,CAAC,2CAgBtD;AAED,iBAAS,aAAa,CAAC,EACrB,SAAS,EACT,QAAQ,EACR,QAAmB,EACnB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,OAAO,CAAC,2CA6BtD;AAED,iBAAS,WAAW,CAAC,EACnB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,KAAK,CAAC,2CAQpD;AAED,iBAAS,UAAU,CAAC,EAClB,SAAS,EACT,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,IAAI,CAAC,2CAkBnD;AAED,iBAAS,eAAe,CAAC,EACvB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,SAAS,CAAC,2CAQxD;AAED,iBAAS,oBAAoB,CAAC,EAC5B,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,cAAc,CAAC,2CAa7D;AAED,iBAAS,sBAAsB,CAAC,EAC9B,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,gBAAgB,CAAC,2CAa/D;AAED,OAAO,EACL,MAAM,EACN,aAAa,EACb,WAAW,EACX,UAAU,EACV,WAAW,EACX,sBAAsB,EACtB,oBAAoB,EACpB,eAAe,EACf,aAAa,EACb,WAAW,GACZ,CAAA"}
@@ -16,9 +16,9 @@ function SelectTrigger({ className, children, ...props }) {
16
16
  return (_jsxs(SelectPrimitive.Trigger, { "data-slot": "select-trigger", className: cn("border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive flex h-9 w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className), ...props, children: [children, _jsx(SelectPrimitive.Icon, { asChild: true, children: _jsx(ChevronDownIcon, { className: "size-4 opacity-50" }) })] }));
17
17
  }
18
18
  function SelectContent({ className, children, position = "popper", ...props }) {
19
- return (_jsx(SelectPrimitive.Portal, { children: _jsxs(SelectPrimitive.Content, { "data-slot": "select-content", className: cn("bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border shadow-md", position === "popper" &&
20
- "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1", className), position: position, ...props, children: [_jsx(SelectScrollUpButton, {}), _jsx(SelectPrimitive.Viewport, { className: cn("p-1", position === "popper" &&
21
- "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1"), children: children }), _jsx(SelectScrollDownButton, {})] }) }));
19
+ return (_jsx(SelectPrimitive.Portal, { children: _jsxs(SelectPrimitive.Content, { "data-slot": "select-content", className: cn("bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-[310] max-h-96 min-w-[8rem] overflow-hidden rounded-md border shadow-md pointer-events-auto", position === "popper" &&
20
+ "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1", className), position: position, onWheel: (e) => e.stopPropagation(), ...props, children: [_jsx(SelectScrollUpButton, {}), _jsx(SelectPrimitive.Viewport, { className: cn("p-1", position === "popper" &&
21
+ "h-full max-h-[var(--radix-select-content-available-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1 overflow-y-auto touch-pan-y"), children: children }), _jsx(SelectScrollDownButton, {})] }) }));
22
22
  }
23
23
  function SelectLabel({ className, ...props }) {
24
24
  return (_jsx(SelectPrimitive.Label, { "data-slot": "select-label", className: cn("px-2 py-1.5 text-sm font-medium", className), ...props }));
@@ -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";
2
- import { cn } from "@myrjfa/state";
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
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,8 +1,8 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { toggleUserWishlist } from "@myrjfa/state";
4
- import { userAtom, userLoadingAtom } from "@myrjfa/state";
5
- import { cn, formatDate, truncateText } from "@myrjfa/state";
3
+ import { toggleUserWishlist } from "@myrjfa/state/dist/lib/actions/actions";
4
+ import { userAtom, userLoadingAtom } from "@myrjfa/state/dist/lib/userAtom";
5
+ import { cn, formatDate, truncateText } from "@myrjfa/state/dist/lib/utils";
6
6
  import { useAtom, useAtomValue } from "jotai";
7
7
  import { Clock, Heart, Loader2, MapPin, Star } from "lucide-react";
8
8
  import Image from "next/image";
@@ -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: "\n fixed bottom-6 right-6 z-[9998]\n flex items-center gap-0\n rounded-full\n bg-white\n shadow-[0_2px_12px_rgba(0,0,0,0.15)]\n hover:shadow-[0_4px_16px_rgba(0,0,0,0.2)]\n border border-gray-200\n transition-all duration-200\n hover:scale-[1.03]\n overflow-hidden\n no-underline\n group\n ", children: [_jsx("span", { className: "\n pl-4 pr-3 py-2.5\n text-sm font-medium text-gray-700\n whitespace-nowrap\n group-hover:text-gray-900\n transition-colors\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
+ }
@@ -1,4 +1,4 @@
1
- export type SocialPlatformType = "instagram" | "youtube" | "linkedin" | "googleBusiness";
1
+ import { SocialPlatformType } from "@myrjfa/state/dist/lib/utils/socialMediaUrl";
2
2
  interface SocialLinkFieldProps {
3
3
  platform: SocialPlatformType;
4
4
  url?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"socialLink.d.ts","sourceRoot":"","sources":["../../src/profile/socialLink.tsx"],"names":[],"mappings":"AAQA,MAAM,MAAM,kBAAkB,GAAG,WAAW,GAAG,SAAS,GAAG,UAAU,GAAG,gBAAgB,CAAC;AAEzF,UAAU,oBAAoB;IAC1B,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,OAAO,CAAC;IACvB,oBAAoB,CAAC,EAAE,CAAC,QAAQ,EAAE,kBAAkB,EAAE,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACvF;AAYD,wBAAgB,eAAe,CAAC,EAC5B,QAAQ,EACR,GAAG,EACH,aAAa,EACb,oBAAoB,GACvB,EAAE,oBAAoB,kDA8CtB"}
1
+ {"version":3,"file":"socialLink.d.ts","sourceRoot":"","sources":["../../src/profile/socialLink.tsx"],"names":[],"mappings":"AAMA,OAAO,EAAsB,kBAAkB,EAAE,MAAM,6CAA6C,CAAC;AAGrG,UAAU,oBAAoB;IAC1B,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,OAAO,CAAC;IACvB,oBAAoB,CAAC,EAAE,CAAC,QAAQ,EAAE,kBAAkB,EAAE,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACvF;AAeD,wBAAgB,eAAe,CAAC,EAC5B,QAAQ,EACR,GAAG,EACH,aAAa,EACb,oBAAoB,GACvB,EAAE,oBAAoB,kDAgFtB"}
@@ -3,30 +3,60 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useState } from "react";
4
4
  import { Input } from "../misc/input";
5
5
  import { Button } from "../misc/button";
6
- import { InstagramIcon, YoutubeIcon, LinkedinIcon, StoreIcon } from "lucide-react";
7
- import { useRouter } from "next/navigation";
6
+ import { GithubIcon, InstagramIcon, StoreIcon, YoutubeIcon } from "lucide-react";
7
+ import { normalizeSocialUrl } from "@myrjfa/state/dist/lib/utils/socialMediaUrl";
8
+ import { FacebookIcon, LinkedinIcon, TwitterIcon } from "react-share";
8
9
  const platformConfig = {
9
- instagram: { label: "Instagram", icon: InstagramIcon, color: "hover:text-pink-600" },
10
- youtube: { label: "YouTube", icon: YoutubeIcon, color: "hover:text-red-600" },
11
- linkedin: { label: "LinkedIn", icon: LinkedinIcon, color: "hover:text-blue-600" },
12
- googleBusiness: { label: "Google Business", icon: StoreIcon, color: "hover:text-green-600" },
10
+ instagram: { label: "Instagram", icon: InstagramIcon, color: "hover:text-pink-600", hint: "Enter username or profile URL" },
11
+ youtube: { label: "YouTube", icon: YoutubeIcon, color: "hover:text-red-600", hint: "Enter @handle or profile URL" },
12
+ twitter: { label: "Twitter", icon: TwitterIcon, color: "hover:text-blue-600", hint: "Enter @handle or profile URL" },
13
+ facebook: { label: "Facebook", icon: FacebookIcon, color: "hover:text-blue-600", hint: "Enter @handle or profile URL" },
14
+ github: { label: "GitHub", icon: GithubIcon, color: "hover:text-blue-600", hint: "Enter @handle or profile URL" },
15
+ linkedin: { label: "LinkedIn", icon: LinkedinIcon, color: "hover:text-blue-600", hint: "Enter username or profile URL" },
16
+ googleBusiness: { label: "Google Business", icon: StoreIcon, color: "hover:text-green-600", hint: "Enter full Google Business URL" },
13
17
  };
14
18
  export function SocialLinkField({ platform, url, isCurrentUser, updateUserSocialLink, }) {
15
- const [input, setInput] = useState(url);
19
+ const [input, setInput] = useState(url !== null && url !== void 0 ? url : "");
20
+ const [urlError, setUrlError] = useState(null);
16
21
  const [loading, setLoading] = useState(false);
17
- const router = useRouter();
18
- const { label, icon: Icon, color } = platformConfig[platform];
22
+ const { label, icon: Icon, color, hint } = platformConfig[platform];
23
+ // #13: Validate and normalize the URL when focus leaves the input
24
+ const handleBlur = () => {
25
+ if (!input.trim() || platform === "googleBusiness")
26
+ return;
27
+ const { url: normalized, error } = normalizeSocialUrl(platform, input);
28
+ setUrlError(error);
29
+ if (!error)
30
+ setInput(normalized); // auto-correct the field to canonical URL
31
+ };
32
+ const handleSave = async () => {
33
+ if (!input.trim() || !updateUserSocialLink)
34
+ return;
35
+ // Final normalization before saving
36
+ let finalUrl = input;
37
+ if (platform !== "googleBusiness") {
38
+ const { url: normalized, error } = normalizeSocialUrl(platform, input);
39
+ if (error) {
40
+ setUrlError(error);
41
+ return;
42
+ }
43
+ finalUrl = normalized;
44
+ setInput(normalized);
45
+ }
46
+ try {
47
+ setLoading(true);
48
+ await updateUserSocialLink(platform, finalUrl);
49
+ setUrlError(null);
50
+ }
51
+ finally {
52
+ setLoading(false);
53
+ }
54
+ };
19
55
  if (isCurrentUser) {
20
- return (_jsxs("div", { className: "flex items-center gap-1", children: [_jsx(Input, { type: "url", placeholder: `${label} profile url`, value: input, onChange: (e) => setInput(e.target.value) }), updateUserSocialLink &&
21
- _jsx(Button, { disabled: loading || !input, onClick: async () => {
22
- try {
23
- setLoading(true);
24
- await updateUserSocialLink(platform, input);
25
- }
26
- finally {
27
- setLoading(false);
28
- }
29
- }, children: "Add" })] }));
56
+ return (_jsxs("div", { className: "flex flex-col gap-1", children: [_jsxs("div", { className: "flex items-center gap-1", children: [_jsx(Icon, { className: `h-5 w-5 shrink-0 text-gray-400 ${color}` }), _jsx(Input, { type: "text", placeholder: hint, value: input, onChange: (e) => {
57
+ setInput(e.target.value);
58
+ setUrlError(null);
59
+ }, onBlur: handleBlur, className: urlError ? "border-red-400 focus:ring-red-400" : "" }), _jsx(Button, { disabled: loading || !input.trim(), onClick: handleSave, children: loading ? "Adding..." : "Add" })] }), urlError && (_jsx("p", { className: "text-xs text-red-500 ml-6", children: urlError }))] }));
30
60
  }
31
61
  else if (url) {
32
62
  return (_jsx("a", { href: url, target: "_blank", rel: "noopener noreferrer", className: `text-gray-500 ${color} flex flex-row gap-1`, children: _jsx(Icon, { className: "h-5 w-5" }) }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@myrjfa/ui",
3
- "version": "2.0.1",
3
+ "version": "2.1.1",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./dist/index.js",
@@ -9,6 +9,7 @@
9
9
  "description": "Shared UI components for my apps",
10
10
  "main": "dist/index.js",
11
11
  "types": "dist/index.d.ts",
12
+ "sideEffects": false,
12
13
  "files": [
13
14
  "dist"
14
15
  ],
@@ -42,7 +43,7 @@
42
43
  "embla-carousel-react": "^8.5.2",
43
44
  "emoji-picker-react": "^4.18.0",
44
45
  "leaflet": "1.9.4",
45
- "pnpm": "^10.14.0",
46
+ "react-easy-crop": "^5.5.6",
46
47
  "react-hook-form": "^7.54.2",
47
48
  "react-icons": "^5.5.0",
48
49
  "react-leaflet": "^5.0.0",
@@ -55,11 +56,11 @@
55
56
  "lucide-react": "*",
56
57
  "next": "*",
57
58
  "react": "*",
58
- "react-dom": "",
59
+ "react-dom": "*",
59
60
  "zod": "*"
60
61
  },
61
62
  "devDependencies": {
62
- "@myrjfa/state": "2.0.1",
63
+ "@myrjfa/state": "2.1.1",
63
64
  "@types/leaflet": "1.9.21",
64
65
  "@types/react": "19.0.10",
65
66
  "@types/react-dom": "^19.0.4",