@soulbatical/tetra-ui 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/AutoCard.d.ts +41 -0
- package/dist/components/AutoCard.d.ts.map +1 -0
- package/dist/components/AutoCard.js +73 -0
- package/dist/components/AutoCard.js.map +1 -0
- package/dist/components/CookieConsent.d.ts +15 -0
- package/dist/components/CookieConsent.d.ts.map +1 -0
- package/dist/components/CookieConsent.js +27 -0
- package/dist/components/CookieConsent.js.map +1 -0
- package/dist/components/GenericListLayout.d.ts +48 -0
- package/dist/components/GenericListLayout.d.ts.map +1 -0
- package/dist/components/GenericListLayout.js +18 -0
- package/dist/components/GenericListLayout.js.map +1 -0
- package/dist/components/ListGenerator.d.ts +41 -0
- package/dist/components/ListGenerator.d.ts.map +1 -0
- package/dist/components/ListGenerator.js +40 -0
- package/dist/components/ListGenerator.js.map +1 -0
- package/dist/components/LoadingStates.d.ts +38 -0
- package/dist/components/LoadingStates.d.ts.map +1 -0
- package/dist/components/LoadingStates.js +51 -0
- package/dist/components/LoadingStates.js.map +1 -0
- package/dist/components/OrgSwitcher.d.ts +15 -0
- package/dist/components/OrgSwitcher.d.ts.map +1 -0
- package/dist/components/OrgSwitcher.js +36 -0
- package/dist/components/OrgSwitcher.js.map +1 -0
- package/dist/components/PageHeader.d.ts +16 -0
- package/dist/components/PageHeader.d.ts.map +1 -0
- package/dist/components/PageHeader.js +10 -0
- package/dist/components/PageHeader.js.map +1 -0
- package/dist/components/ProtectedRoute.d.ts +9 -0
- package/dist/components/ProtectedRoute.d.ts.map +1 -0
- package/dist/components/ProtectedRoute.js +30 -0
- package/dist/components/ProtectedRoute.js.map +1 -0
- package/dist/components/StatusBadge.d.ts +15 -0
- package/dist/components/StatusBadge.d.ts.map +1 -0
- package/dist/components/StatusBadge.js +79 -0
- package/dist/components/StatusBadge.js.map +1 -0
- package/dist/components/ThemeToggle.d.ts +14 -0
- package/dist/components/ThemeToggle.d.ts.map +1 -0
- package/dist/components/ThemeToggle.js +20 -0
- package/dist/components/ThemeToggle.js.map +1 -0
- package/dist/hooks/use-mobile.d.ts +2 -0
- package/dist/hooks/use-mobile.d.ts.map +1 -0
- package/dist/hooks/use-mobile.js +17 -0
- package/dist/hooks/use-mobile.js.map +1 -0
- package/dist/hooks/useAuth.d.ts +34 -0
- package/dist/hooks/useAuth.d.ts.map +1 -0
- package/dist/hooks/useAuth.js +156 -0
- package/dist/hooks/useAuth.js.map +1 -0
- package/dist/hooks/useCacheInvalidation.d.ts +25 -0
- package/dist/hooks/useCacheInvalidation.d.ts.map +1 -0
- package/dist/hooks/useCacheInvalidation.js +57 -0
- package/dist/hooks/useCacheInvalidation.js.map +1 -0
- package/dist/hooks/useDebounce.d.ts +2 -0
- package/dist/hooks/useDebounce.d.ts.map +1 -0
- package/dist/hooks/useDebounce.js +15 -0
- package/dist/hooks/useDebounce.js.map +1 -0
- package/dist/hooks/useDialog.d.ts +26 -0
- package/dist/hooks/useDialog.d.ts.map +1 -0
- package/dist/hooks/useDialog.js +37 -0
- package/dist/hooks/useDialog.js.map +1 -0
- package/dist/hooks/useFilterConfigs.d.ts +81 -0
- package/dist/hooks/useFilterConfigs.d.ts.map +1 -0
- package/dist/hooks/useFilterConfigs.js +68 -0
- package/dist/hooks/useFilterConfigs.js.map +1 -0
- package/dist/hooks/useFormSubmit.d.ts +79 -0
- package/dist/hooks/useFormSubmit.d.ts.map +1 -0
- package/dist/hooks/useFormSubmit.js +57 -0
- package/dist/hooks/useFormSubmit.js.map +1 -0
- package/dist/hooks/useGenericList.d.ts +62 -0
- package/dist/hooks/useGenericList.d.ts.map +1 -0
- package/dist/hooks/useGenericList.js +75 -0
- package/dist/hooks/useGenericList.js.map +1 -0
- package/dist/hooks/useGenericListWithConfig.d.ts +65 -0
- package/dist/hooks/useGenericListWithConfig.d.ts.map +1 -0
- package/dist/hooks/useGenericListWithConfig.js +98 -0
- package/dist/hooks/useGenericListWithConfig.js.map +1 -0
- package/dist/hooks/useInfiniteScroll.d.ts +11 -0
- package/dist/hooks/useInfiniteScroll.d.ts.map +1 -0
- package/dist/hooks/useInfiniteScroll.js +63 -0
- package/dist/hooks/useInfiniteScroll.js.map +1 -0
- package/dist/hooks/useOrganizations.d.ts +27 -0
- package/dist/hooks/useOrganizations.d.ts.map +1 -0
- package/dist/hooks/useOrganizations.js +56 -0
- package/dist/hooks/useOrganizations.js.map +1 -0
- package/dist/hooks/usePageContext.d.ts +29 -0
- package/dist/hooks/usePageContext.d.ts.map +1 -0
- package/dist/hooks/usePageContext.js +32 -0
- package/dist/hooks/usePageContext.js.map +1 -0
- package/dist/hooks/usePersistedQueryState.d.ts +35 -0
- package/dist/hooks/usePersistedQueryState.d.ts.map +1 -0
- package/dist/hooks/usePersistedQueryState.js +187 -0
- package/dist/hooks/usePersistedQueryState.js.map +1 -0
- package/dist/hooks/useQueryState.d.ts +40 -0
- package/dist/hooks/useQueryState.d.ts.map +1 -0
- package/dist/hooks/useQueryState.js +246 -0
- package/dist/hooks/useQueryState.js.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +43 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/utils.d.ts +3 -0
- package/dist/lib/utils.d.ts.map +1 -0
- package/dist/lib/utils.js +6 -0
- package/dist/lib/utils.js.map +1 -0
- package/dist/providers/QueryProvider.d.ts +9 -0
- package/dist/providers/QueryProvider.d.ts.map +1 -0
- package/dist/providers/QueryProvider.js +20 -0
- package/dist/providers/QueryProvider.js.map +1 -0
- package/dist/providers/ThemeProvider.d.ts +15 -0
- package/dist/providers/ThemeProvider.d.ts.map +1 -0
- package/dist/providers/ThemeProvider.js +13 -0
- package/dist/providers/ThemeProvider.js.map +1 -0
- package/dist/services/apiClient.d.ts +20 -0
- package/dist/services/apiClient.d.ts.map +1 -0
- package/dist/services/apiClient.js +85 -0
- package/dist/services/apiClient.js.map +1 -0
- package/dist/types/index.d.ts +119 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +5 -0
- package/dist/types/index.js.map +1 -0
- package/package.json +68 -0
- package/src/styles/dark-mode.css +135 -0
- package/src/styles/theme.css +105 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useEffect, useRef, useCallback } from 'react';
|
|
3
|
+
export const useInfiniteScroll = ({ onLoadMore, hasMore, threshold = 200, enabled = true }) => {
|
|
4
|
+
const loadingRef = useRef(false);
|
|
5
|
+
const handleScroll = useCallback(() => {
|
|
6
|
+
if (!enabled || !hasMore || loadingRef.current)
|
|
7
|
+
return;
|
|
8
|
+
const scrollHeight = document.documentElement.scrollHeight;
|
|
9
|
+
const scrollTop = document.documentElement.scrollTop;
|
|
10
|
+
const clientHeight = window.innerHeight;
|
|
11
|
+
// Check if user has scrolled near the bottom
|
|
12
|
+
if (scrollHeight - scrollTop - clientHeight < threshold) {
|
|
13
|
+
loadingRef.current = true;
|
|
14
|
+
onLoadMore();
|
|
15
|
+
// Reset loading flag after a short delay
|
|
16
|
+
setTimeout(() => {
|
|
17
|
+
loadingRef.current = false;
|
|
18
|
+
}, 100);
|
|
19
|
+
}
|
|
20
|
+
}, [onLoadMore, hasMore, threshold, enabled]);
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
if (!enabled)
|
|
23
|
+
return;
|
|
24
|
+
// Add scroll listener
|
|
25
|
+
window.addEventListener('scroll', handleScroll);
|
|
26
|
+
// Check immediately in case content is shorter than viewport
|
|
27
|
+
handleScroll();
|
|
28
|
+
return () => {
|
|
29
|
+
window.removeEventListener('scroll', handleScroll);
|
|
30
|
+
};
|
|
31
|
+
}, [handleScroll, enabled]);
|
|
32
|
+
// Return a sentinel element that can be observed
|
|
33
|
+
const observerRef = useRef(null);
|
|
34
|
+
const sentinelRef = useCallback((node) => {
|
|
35
|
+
// Cleanup previous observer
|
|
36
|
+
if (observerRef.current) {
|
|
37
|
+
observerRef.current.disconnect();
|
|
38
|
+
}
|
|
39
|
+
if (!node || !enabled)
|
|
40
|
+
return;
|
|
41
|
+
// Create new observer
|
|
42
|
+
observerRef.current = new IntersectionObserver((entries) => {
|
|
43
|
+
if (entries[0].isIntersecting && hasMore && !loadingRef.current) {
|
|
44
|
+
loadingRef.current = true;
|
|
45
|
+
onLoadMore();
|
|
46
|
+
setTimeout(() => {
|
|
47
|
+
loadingRef.current = false;
|
|
48
|
+
}, 100);
|
|
49
|
+
}
|
|
50
|
+
}, { threshold: 0.1 });
|
|
51
|
+
observerRef.current.observe(node);
|
|
52
|
+
}, [onLoadMore, hasMore, enabled]);
|
|
53
|
+
// Cleanup on unmount
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
return () => {
|
|
56
|
+
if (observerRef.current) {
|
|
57
|
+
observerRef.current.disconnect();
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
}, []);
|
|
61
|
+
return { sentinelRef };
|
|
62
|
+
};
|
|
63
|
+
//# sourceMappingURL=useInfiniteScroll.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useInfiniteScroll.js","sourceRoot":"","sources":["../../src/hooks/useInfiniteScroll.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface Organization {
|
|
2
|
+
id: string;
|
|
3
|
+
name: string;
|
|
4
|
+
slug?: string;
|
|
5
|
+
logo_url?: string;
|
|
6
|
+
role?: string;
|
|
7
|
+
}
|
|
8
|
+
interface UserProfile {
|
|
9
|
+
id: string;
|
|
10
|
+
email: string;
|
|
11
|
+
firstname?: string;
|
|
12
|
+
lastname?: string;
|
|
13
|
+
avatar_url?: string;
|
|
14
|
+
active_organization_id?: string;
|
|
15
|
+
is_superadmin?: boolean;
|
|
16
|
+
organizations?: Organization[];
|
|
17
|
+
}
|
|
18
|
+
export declare function useOrganizations(): {
|
|
19
|
+
organizations: Organization[];
|
|
20
|
+
currentOrg: Organization | null;
|
|
21
|
+
profile: UserProfile | null;
|
|
22
|
+
loading: boolean;
|
|
23
|
+
switchOrganization: (organizationId: string) => Promise<void>;
|
|
24
|
+
refetch: () => Promise<void>;
|
|
25
|
+
};
|
|
26
|
+
export {};
|
|
27
|
+
//# sourceMappingURL=useOrganizations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useOrganizations.d.ts","sourceRoot":"","sources":["../../src/hooks/useOrganizations.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,UAAU,WAAW;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,aAAa,CAAC,EAAE,YAAY,EAAE,CAAC;CAChC;AAED,wBAAgB,gBAAgB;;;;;yCAmCL,MAAM;;EAuBhC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useState, useEffect, useCallback } from "react";
|
|
3
|
+
import { apiClient } from "../services/apiClient.js";
|
|
4
|
+
import { waitForAuthReady } from "./useAuth.js";
|
|
5
|
+
export function useOrganizations() {
|
|
6
|
+
const [organizations, setOrganizations] = useState([]);
|
|
7
|
+
const [currentOrg, setCurrentOrg] = useState(null);
|
|
8
|
+
const [profile, setProfile] = useState(null);
|
|
9
|
+
const [loading, setLoading] = useState(true);
|
|
10
|
+
const fetchProfile = useCallback(async () => {
|
|
11
|
+
try {
|
|
12
|
+
await waitForAuthReady();
|
|
13
|
+
const res = await apiClient.get("/api/user/profile");
|
|
14
|
+
if (res.success && res.data) {
|
|
15
|
+
setProfile(res.data);
|
|
16
|
+
if (res.data.organizations) {
|
|
17
|
+
setOrganizations(res.data.organizations);
|
|
18
|
+
const active = res.data.organizations.find((o) => o.id === res.data.active_organization_id);
|
|
19
|
+
setCurrentOrg(active || res.data.organizations[0] || null);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
// Not authenticated or backend unavailable
|
|
25
|
+
}
|
|
26
|
+
finally {
|
|
27
|
+
setLoading(false);
|
|
28
|
+
}
|
|
29
|
+
}, []);
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
fetchProfile();
|
|
32
|
+
}, [fetchProfile]);
|
|
33
|
+
const switchOrganization = useCallback(async (organizationId) => {
|
|
34
|
+
try {
|
|
35
|
+
await apiClient.put("/api/user/organizations/switch", {
|
|
36
|
+
organizationId,
|
|
37
|
+
});
|
|
38
|
+
const switched = organizations.find((o) => o.id === organizationId);
|
|
39
|
+
if (switched)
|
|
40
|
+
setCurrentOrg(switched);
|
|
41
|
+
window.location.reload();
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
console.error("Failed to switch organization:", error);
|
|
45
|
+
}
|
|
46
|
+
}, [organizations]);
|
|
47
|
+
return {
|
|
48
|
+
organizations,
|
|
49
|
+
currentOrg,
|
|
50
|
+
profile,
|
|
51
|
+
loading,
|
|
52
|
+
switchOrganization,
|
|
53
|
+
refetch: fetchProfile,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=useOrganizations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useOrganizations.js","sourceRoot":"","sources":["../../src/hooks/useOrganizations.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AACzD,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAqBhD,MAAM,UAAU,gBAAgB;IAC9B,MAAM,CAAC,aAAa,EAAE,gBAAgB,CAAC,GAAG,QAAQ,CAAiB,EAAE,CAAC,CAAC;IACvE,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC,GAAG,QAAQ,CAAsB,IAAI,CAAC,CAAC;IACxE,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAqB,IAAI,CAAC,CAAC;IACjE,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IAE7C,MAAM,YAAY,GAAG,WAAW,CAAC,KAAK,IAAI,EAAE;QAC1C,IAAI,CAAC;YACH,MAAM,gBAAgB,EAAE,CAAC;YACzB,MAAM,GAAG,GAAG,MAAM,SAAS,CAAC,GAAG,CAG5B,mBAAmB,CAAC,CAAC;YACxB,IAAI,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC;gBAC5B,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;gBACrB,IAAI,GAAG,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;oBAC3B,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;oBACzC,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,CACxC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,IAAI,CAAC,sBAAsB,CAChD,CAAC;oBACF,aAAa,CAAC,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC;gBAC7D,CAAC;YACH,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,2CAA2C;QAC7C,CAAC;gBAAS,CAAC;YACT,UAAU,CAAC,KAAK,CAAC,CAAC;QACpB,CAAC;IACH,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,SAAS,CAAC,GAAG,EAAE;QACb,YAAY,EAAE,CAAC;IACjB,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC;IAEnB,MAAM,kBAAkB,GAAG,WAAW,CACpC,KAAK,EAAE,cAAsB,EAAE,EAAE;QAC/B,IAAI,CAAC;YACH,MAAM,SAAS,CAAC,GAAG,CAAC,gCAAgC,EAAE;gBACpD,cAAc;aACf,CAAC,CAAC;YACH,MAAM,QAAQ,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,cAAc,CAAC,CAAC;YACpE,IAAI,QAAQ;gBAAE,aAAa,CAAC,QAAQ,CAAC,CAAC;YACtC,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;QAC3B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,gCAAgC,EAAE,KAAK,CAAC,CAAC;QACzD,CAAC;IACH,CAAC,EACD,CAAC,aAAa,CAAC,CAChB,CAAC;IAEF,OAAO;QACL,aAAa;QACb,UAAU;QACV,OAAO;QACP,OAAO;QACP,kBAAkB;QAClB,OAAO,EAAE,YAAY;KACtB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
export interface PageContext {
|
|
3
|
+
/** Page identifier (e.g. "review/styles", "campaigns/detail") */
|
|
4
|
+
page: string;
|
|
5
|
+
/** Brand name if on a brand-related page */
|
|
6
|
+
brandName?: string;
|
|
7
|
+
/** Brand slug if on a brand-related page */
|
|
8
|
+
brandSlug?: string;
|
|
9
|
+
/** Project name if on a project page */
|
|
10
|
+
projectName?: string;
|
|
11
|
+
/** Project ID */
|
|
12
|
+
projectId?: string;
|
|
13
|
+
/** Campaign name if on a campaign page */
|
|
14
|
+
campaignName?: string;
|
|
15
|
+
/** Campaign ID */
|
|
16
|
+
campaignId?: string;
|
|
17
|
+
/** Human-readable summary of what's on the page */
|
|
18
|
+
summary?: string;
|
|
19
|
+
/** Arbitrary extra context */
|
|
20
|
+
[key: string]: string | number | boolean | undefined;
|
|
21
|
+
}
|
|
22
|
+
export declare function PageContextProvider({ children }: {
|
|
23
|
+
children: ReactNode;
|
|
24
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
25
|
+
export declare function usePageContext(): {
|
|
26
|
+
pageContext: PageContext | null;
|
|
27
|
+
setPageContext: (ctx: PageContext | null) => void;
|
|
28
|
+
};
|
|
29
|
+
//# sourceMappingURL=usePageContext.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usePageContext.d.ts","sourceRoot":"","sources":["../../src/hooks/usePageContext.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAIvC,MAAM,WAAW,WAAW;IAC1B,iEAAiE;IACjE,IAAI,EAAE,MAAM,CAAC;IACb,4CAA4C;IAC5C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4CAA4C;IAC5C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,wCAAwC;IACxC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iBAAiB;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0CAA0C;IAC1C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kBAAkB;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mDAAmD;IACnD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,8BAA8B;IAC9B,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC;CACtD;AA+BD,wBAAgB,mBAAmB,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,SAAS,CAAA;CAAE,2CAUxE;AAED,wBAAgB,cAAc;;0BAjBN,WAAW,GAAG,IAAI,KAAK,IAAI;EAsBlD"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { createContext, useContext, useCallback, useSyncExternalStore } from 'react';
|
|
4
|
+
let currentContext = null;
|
|
5
|
+
const listeners = new Set();
|
|
6
|
+
function subscribe(listener) {
|
|
7
|
+
listeners.add(listener);
|
|
8
|
+
return () => listeners.delete(listener);
|
|
9
|
+
}
|
|
10
|
+
function getSnapshot() {
|
|
11
|
+
return currentContext;
|
|
12
|
+
}
|
|
13
|
+
function setContext(ctx) {
|
|
14
|
+
currentContext = ctx;
|
|
15
|
+
listeners.forEach((l) => l());
|
|
16
|
+
}
|
|
17
|
+
// ─── Context (for the setter — must be inside provider tree) ──
|
|
18
|
+
const PageContextSetterCtx = createContext({
|
|
19
|
+
setPageContext: setContext,
|
|
20
|
+
});
|
|
21
|
+
export function PageContextProvider({ children }) {
|
|
22
|
+
const setPageContext = useCallback((ctx) => {
|
|
23
|
+
setContext(ctx);
|
|
24
|
+
}, []);
|
|
25
|
+
return (_jsx(PageContextSetterCtx.Provider, { value: { setPageContext }, children: children }));
|
|
26
|
+
}
|
|
27
|
+
export function usePageContext() {
|
|
28
|
+
const { setPageContext } = useContext(PageContextSetterCtx);
|
|
29
|
+
const pageContext = useSyncExternalStore(subscribe, getSnapshot, () => null);
|
|
30
|
+
return { pageContext, setPageContext };
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=usePageContext.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usePageContext.js","sourceRoot":"","sources":["../../src/hooks/usePageContext.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAC;AA8BrF,IAAI,cAAc,GAAuB,IAAI,CAAC;AAC9C,MAAM,SAAS,GAAG,IAAI,GAAG,EAAY,CAAC;AAEtC,SAAS,SAAS,CAAC,QAAkB;IACnC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACxB,OAAO,GAAG,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;AAC1C,CAAC;AAED,SAAS,WAAW;IAClB,OAAO,cAAc,CAAC;AACxB,CAAC;AAED,SAAS,UAAU,CAAC,GAAuB;IACzC,cAAc,GAAG,GAAG,CAAC;IACrB,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;AAChC,CAAC;AAED,iEAAiE;AAEjE,MAAM,oBAAoB,GAAG,aAAa,CAEvC;IACD,cAAc,EAAE,UAAU;CAC3B,CAAC,CAAC;AAEH,MAAM,UAAU,mBAAmB,CAAC,EAAE,QAAQ,EAA2B;IACvE,MAAM,cAAc,GAAG,WAAW,CAAC,CAAC,GAAuB,EAAE,EAAE;QAC7D,UAAU,CAAC,GAAG,CAAC,CAAC;IAClB,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,OAAO,CACL,KAAC,oBAAoB,CAAC,QAAQ,IAAC,KAAK,EAAE,EAAE,cAAc,EAAE,YACrD,QAAQ,GACqB,CACjC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,MAAM,EAAE,cAAc,EAAE,GAAG,UAAU,CAAC,oBAAoB,CAAC,CAAC;IAC5D,MAAM,WAAW,GAAG,oBAAoB,CAAC,SAAS,EAAE,WAAW,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IAE7E,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,CAAC;AACzC,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* usePersistedQueryState - Query state with sessionStorage persistence
|
|
3
|
+
*
|
|
4
|
+
* Adapted for Next.js App Router (useSearchParams + useRouter).
|
|
5
|
+
*
|
|
6
|
+
* Features:
|
|
7
|
+
* - Persists filters to sessionStorage per feature
|
|
8
|
+
* - Restores filters when navigating back to a page without query params
|
|
9
|
+
* - Works with breadcrumbs, browser back/forward, sidebar menu
|
|
10
|
+
* - Each feature has independent filter memory
|
|
11
|
+
*
|
|
12
|
+
* @param featureName - Unique identifier for the feature (e.g., 'campaigns', 'orders')
|
|
13
|
+
* @param defaultValues - Default filter values
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* const { filters, setFilter } = usePersistedQueryState('campaigns', {
|
|
17
|
+
* search: '',
|
|
18
|
+
* status: 'all',
|
|
19
|
+
* sortBy: 'date-desc'
|
|
20
|
+
* });
|
|
21
|
+
*/
|
|
22
|
+
export declare function usePersistedQueryState<T extends Record<string, any>>(featureName: string, defaultValues: T): {
|
|
23
|
+
filters: T;
|
|
24
|
+
setFilter: <K extends keyof T>(key: K, value: T[K]) => void;
|
|
25
|
+
resetFilters: () => void;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Clear all persisted filters (useful for logout)
|
|
29
|
+
*/
|
|
30
|
+
export declare function clearAllPersistedFilters(): void;
|
|
31
|
+
/**
|
|
32
|
+
* Get persisted filters for a feature (useful for debugging)
|
|
33
|
+
*/
|
|
34
|
+
export declare function getPersistedFilters(featureName: string): Record<string, any> | null;
|
|
35
|
+
//# sourceMappingURL=usePersistedQueryState.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usePersistedQueryState.d.ts","sourceRoot":"","sources":["../../src/hooks/usePersistedQueryState.ts"],"names":[],"mappings":"AAOA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAClE,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,CAAC,GACf;IACD,OAAO,EAAE,CAAC,CAAC;IACX,SAAS,EAAE,CAAC,CAAC,SAAS,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;IAC5D,YAAY,EAAE,MAAM,IAAI,CAAC;CAC1B,CA2JA;AAED;;GAEG;AACH,wBAAgB,wBAAwB,IAAI,IAAI,CAW/C;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,CAOnF"}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useSearchParams, useRouter, usePathname } from 'next/navigation.js';
|
|
3
|
+
import { useCallback, useMemo, useEffect, useRef } from 'react';
|
|
4
|
+
const STORAGE_PREFIX = 'tetra:filters:';
|
|
5
|
+
/**
|
|
6
|
+
* usePersistedQueryState - Query state with sessionStorage persistence
|
|
7
|
+
*
|
|
8
|
+
* Adapted for Next.js App Router (useSearchParams + useRouter).
|
|
9
|
+
*
|
|
10
|
+
* Features:
|
|
11
|
+
* - Persists filters to sessionStorage per feature
|
|
12
|
+
* - Restores filters when navigating back to a page without query params
|
|
13
|
+
* - Works with breadcrumbs, browser back/forward, sidebar menu
|
|
14
|
+
* - Each feature has independent filter memory
|
|
15
|
+
*
|
|
16
|
+
* @param featureName - Unique identifier for the feature (e.g., 'campaigns', 'orders')
|
|
17
|
+
* @param defaultValues - Default filter values
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* const { filters, setFilter } = usePersistedQueryState('campaigns', {
|
|
21
|
+
* search: '',
|
|
22
|
+
* status: 'all',
|
|
23
|
+
* sortBy: 'date-desc'
|
|
24
|
+
* });
|
|
25
|
+
*/
|
|
26
|
+
export function usePersistedQueryState(featureName, defaultValues) {
|
|
27
|
+
const searchParams = useSearchParams();
|
|
28
|
+
const router = useRouter();
|
|
29
|
+
const pathname = usePathname();
|
|
30
|
+
const storageKey = `${STORAGE_PREFIX}${featureName}`;
|
|
31
|
+
const isInitialMount = useRef(true);
|
|
32
|
+
const hasRestoredFilters = useRef(false);
|
|
33
|
+
/** Push new search params to URL (replace, no scroll) */
|
|
34
|
+
const pushParams = useCallback((next, options) => {
|
|
35
|
+
const qs = next.toString();
|
|
36
|
+
router.replace(`${pathname}${qs ? `?${qs}` : ''}`, { scroll: false });
|
|
37
|
+
}, [router, pathname]);
|
|
38
|
+
// Check if URL has any filter params (not just any params)
|
|
39
|
+
const hasUrlFilters = useMemo(() => {
|
|
40
|
+
const filterKeys = Object.keys(defaultValues);
|
|
41
|
+
return filterKeys.some(key => searchParams.has(key));
|
|
42
|
+
}, [searchParams, defaultValues]);
|
|
43
|
+
// Restore filters from sessionStorage on initial mount if URL has no filters
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
if (!isInitialMount.current || hasRestoredFilters.current)
|
|
46
|
+
return;
|
|
47
|
+
isInitialMount.current = false;
|
|
48
|
+
// Only restore if URL doesn't have filter params
|
|
49
|
+
if (!hasUrlFilters) {
|
|
50
|
+
try {
|
|
51
|
+
const stored = sessionStorage.getItem(storageKey);
|
|
52
|
+
if (stored) {
|
|
53
|
+
const savedFilters = JSON.parse(stored);
|
|
54
|
+
// Only restore non-default values
|
|
55
|
+
const hasNonDefaultValues = Object.entries(savedFilters).some(([key, value]) => {
|
|
56
|
+
const defaultValue = defaultValues[key];
|
|
57
|
+
return value !== defaultValue && value !== '' && value !== undefined;
|
|
58
|
+
});
|
|
59
|
+
if (hasNonDefaultValues) {
|
|
60
|
+
// Restore filters to URL
|
|
61
|
+
const next = new URLSearchParams(searchParams.toString());
|
|
62
|
+
Object.entries(savedFilters).forEach(([key, value]) => {
|
|
63
|
+
if (value !== undefined && value !== '' && value !== null) {
|
|
64
|
+
if (typeof value === 'object') {
|
|
65
|
+
next.set(key, JSON.stringify(value));
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
next.set(key, String(value));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
pushParams(next, { replace: true });
|
|
73
|
+
hasRestoredFilters.current = true;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
catch (e) {
|
|
78
|
+
console.warn(`[usePersistedQueryState] Failed to restore filters for ${featureName}:`, e);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}, [featureName, hasUrlFilters, storageKey, defaultValues, searchParams, pushParams]);
|
|
82
|
+
// Parse current filters from URL
|
|
83
|
+
const filters = useMemo(() => {
|
|
84
|
+
const result = { ...defaultValues };
|
|
85
|
+
Object.keys(defaultValues).forEach((key) => {
|
|
86
|
+
const value = searchParams.get(key);
|
|
87
|
+
if (value !== null) {
|
|
88
|
+
try {
|
|
89
|
+
result[key] = JSON.parse(value);
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
// Keep as string - don't convert to number
|
|
93
|
+
result[key] = value;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
return result;
|
|
98
|
+
}, [searchParams, defaultValues]);
|
|
99
|
+
// Save filters to sessionStorage whenever they change
|
|
100
|
+
useEffect(() => {
|
|
101
|
+
// Skip on initial mount before restoration
|
|
102
|
+
if (isInitialMount.current)
|
|
103
|
+
return;
|
|
104
|
+
// Only save non-default filter values
|
|
105
|
+
const filtersToSave = {};
|
|
106
|
+
let hasFiltersToSave = false;
|
|
107
|
+
Object.entries(filters).forEach(([key, value]) => {
|
|
108
|
+
const defaultValue = defaultValues[key];
|
|
109
|
+
// Save if different from default and not empty
|
|
110
|
+
if (value !== defaultValue && value !== '' && value !== undefined) {
|
|
111
|
+
filtersToSave[key] = value;
|
|
112
|
+
hasFiltersToSave = true;
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
if (hasFiltersToSave) {
|
|
116
|
+
try {
|
|
117
|
+
sessionStorage.setItem(storageKey, JSON.stringify(filtersToSave));
|
|
118
|
+
}
|
|
119
|
+
catch (e) {
|
|
120
|
+
console.warn(`[usePersistedQueryState] Failed to save filters for ${featureName}:`, e);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
// Clear storage if all filters are default
|
|
125
|
+
sessionStorage.removeItem(storageKey);
|
|
126
|
+
}
|
|
127
|
+
}, [filters, defaultValues, storageKey, featureName]);
|
|
128
|
+
// Set a single filter
|
|
129
|
+
const setFilter = useCallback((key, value) => {
|
|
130
|
+
const next = new URLSearchParams(searchParams.toString());
|
|
131
|
+
if (value === undefined || value === '' || value === null) {
|
|
132
|
+
next.delete(String(key));
|
|
133
|
+
}
|
|
134
|
+
else if (typeof value === 'object') {
|
|
135
|
+
next.set(String(key), JSON.stringify(value));
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
next.set(String(key), String(value));
|
|
139
|
+
}
|
|
140
|
+
pushParams(next);
|
|
141
|
+
}, [searchParams, pushParams]);
|
|
142
|
+
// Reset all filters to defaults
|
|
143
|
+
const resetFilters = useCallback(() => {
|
|
144
|
+
// Clear URL params
|
|
145
|
+
const next = new URLSearchParams();
|
|
146
|
+
// Preserve non-filter params
|
|
147
|
+
searchParams.forEach((value, key) => {
|
|
148
|
+
if (!Object.keys(defaultValues).includes(key)) {
|
|
149
|
+
next.set(key, value);
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
pushParams(next);
|
|
153
|
+
// Clear sessionStorage
|
|
154
|
+
sessionStorage.removeItem(storageKey);
|
|
155
|
+
}, [searchParams, pushParams, defaultValues, storageKey]);
|
|
156
|
+
return {
|
|
157
|
+
filters,
|
|
158
|
+
setFilter,
|
|
159
|
+
resetFilters,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Clear all persisted filters (useful for logout)
|
|
164
|
+
*/
|
|
165
|
+
export function clearAllPersistedFilters() {
|
|
166
|
+
const keysToRemove = [];
|
|
167
|
+
for (let i = 0; i < sessionStorage.length; i++) {
|
|
168
|
+
const key = sessionStorage.key(i);
|
|
169
|
+
if (key?.startsWith(STORAGE_PREFIX)) {
|
|
170
|
+
keysToRemove.push(key);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
keysToRemove.forEach(key => sessionStorage.removeItem(key));
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Get persisted filters for a feature (useful for debugging)
|
|
177
|
+
*/
|
|
178
|
+
export function getPersistedFilters(featureName) {
|
|
179
|
+
try {
|
|
180
|
+
const stored = sessionStorage.getItem(`${STORAGE_PREFIX}${featureName}`);
|
|
181
|
+
return stored ? JSON.parse(stored) : null;
|
|
182
|
+
}
|
|
183
|
+
catch {
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
//# sourceMappingURL=usePersistedQueryState.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usePersistedQueryState.js","sourceRoot":"","sources":["../../src/hooks/usePersistedQueryState.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
interface QueryStateReturn<T extends Record<string, any>> {
|
|
2
|
+
params: URLSearchParams;
|
|
3
|
+
setParam: <K extends keyof T>(key: K, value: T[K] | undefined) => void;
|
|
4
|
+
setParams: (params: Partial<T>) => void;
|
|
5
|
+
getParam: <K extends keyof T>(key: K) => T[K] | undefined;
|
|
6
|
+
clearParam: <K extends keyof T>(key: K) => void;
|
|
7
|
+
clearParams: () => void;
|
|
8
|
+
pagination: {
|
|
9
|
+
page: number;
|
|
10
|
+
perPage: number;
|
|
11
|
+
setPage: (page: number) => void;
|
|
12
|
+
setPerPage: (perPage: number) => void;
|
|
13
|
+
};
|
|
14
|
+
sorting: {
|
|
15
|
+
sort?: string;
|
|
16
|
+
order: 'asc' | 'desc';
|
|
17
|
+
setSort: (field: string | undefined, order?: 'asc' | 'desc') => void;
|
|
18
|
+
};
|
|
19
|
+
search: {
|
|
20
|
+
query?: string;
|
|
21
|
+
setQuery: (query: string | undefined) => void;
|
|
22
|
+
};
|
|
23
|
+
filters: T;
|
|
24
|
+
setFilters: (filters: Partial<T>) => void;
|
|
25
|
+
clearFilters: () => void;
|
|
26
|
+
hasFilters: boolean;
|
|
27
|
+
buildQueryString: () => string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Hook for managing query parameters in the URL with type safety.
|
|
31
|
+
* Adapted for Next.js App Router (useSearchParams + useRouter).
|
|
32
|
+
*/
|
|
33
|
+
export declare function useQueryState<T extends Record<string, any> = Record<string, any>>(): QueryStateReturn<T>;
|
|
34
|
+
export declare function useQueryState<T extends Record<string, any>>(defaultValues: T): {
|
|
35
|
+
filters: T;
|
|
36
|
+
setFilter: <K extends keyof T>(key: K, value: T[K]) => void;
|
|
37
|
+
resetFilters: () => void;
|
|
38
|
+
};
|
|
39
|
+
export {};
|
|
40
|
+
//# sourceMappingURL=useQueryState.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useQueryState.d.ts","sourceRoot":"","sources":["../../src/hooks/useQueryState.ts"],"names":[],"mappings":"AAKA,UAAU,gBAAgB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IACtD,MAAM,EAAE,eAAe,CAAC;IACxB,QAAQ,EAAE,CAAC,CAAC,SAAS,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,SAAS,KAAK,IAAI,CAAC;IACvE,SAAS,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;IACxC,QAAQ,EAAE,CAAC,CAAC,SAAS,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IAC1D,UAAU,EAAE,CAAC,CAAC,SAAS,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,KAAK,IAAI,CAAC;IAChD,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB,UAAU,EAAE;QACV,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;QAChC,UAAU,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;KACvC,CAAC;IACF,OAAO,EAAE;QACP,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,KAAK,GAAG,MAAM,CAAC;QACtB,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,KAAK,CAAC,EAAE,KAAK,GAAG,MAAM,KAAK,IAAI,CAAC;KACtE,CAAC;IACF,MAAM,EAAE;QACN,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC;KAC/C,CAAC;IACF,OAAO,EAAE,CAAC,CAAC;IACX,UAAU,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;IAC1C,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,UAAU,EAAE,OAAO,CAAC;IACpB,gBAAgB,EAAE,MAAM,MAAM,CAAC;CAChC;AAcD;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAChD,gBAAgB,CAAC,CAAC,CAAC,CAAC;AACzB,wBAAgB,aAAa,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACzD,aAAa,EAAE,CAAC,GACf;IACD,OAAO,EAAE,CAAC,CAAC;IACX,SAAS,EAAE,CAAC,CAAC,SAAS,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;IAC5D,YAAY,EAAE,MAAM,IAAI,CAAC;CAC1B,CAAC"}
|