@c15t/dev-tools 0.0.1-rc.3
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/.turbo/turbo-build.log +56 -0
- package/.turbo/turbo-fmt.log +6 -0
- package/.turbo/turbo-lint.log +73 -0
- package/CHANGELOG.md +23 -0
- package/LICENSE.md +595 -0
- package/README.md +47 -0
- package/dist/components/error-state.cjs +126 -0
- package/dist/components/error-state.d.ts +6 -0
- package/dist/components/error-state.d.ts.map +1 -0
- package/dist/components/error-state.js +110 -0
- package/dist/components/header.cjs +88 -0
- package/dist/components/header.d.ts +8 -0
- package/dist/components/header.d.ts.map +1 -0
- package/dist/components/header.js +56 -0
- package/dist/components/ui/accordion.cjs +119 -0
- package/dist/components/ui/accordion.d.ts +34 -0
- package/dist/components/ui/accordion.d.ts.map +1 -0
- package/dist/components/ui/accordion.js +84 -0
- package/dist/components/ui/alert.cjs +104 -0
- package/dist/components/ui/alert.d.ts +25 -0
- package/dist/components/ui/alert.d.ts.map +1 -0
- package/dist/components/ui/alert.js +67 -0
- package/dist/components/ui/button.cjs +95 -0
- package/dist/components/ui/button.d.ts +28 -0
- package/dist/components/ui/button.d.ts.map +1 -0
- package/dist/components/ui/button.js +55 -0
- package/dist/components/ui/card.cjs +120 -0
- package/dist/components/ui/card.d.ts +30 -0
- package/dist/components/ui/card.d.ts.map +1 -0
- package/dist/components/ui/card.js +99 -0
- package/dist/components/ui/expandable-tabs.cjs +201 -0
- package/dist/components/ui/expandable-tabs.d.ts +29 -0
- package/dist/components/ui/expandable-tabs.d.ts.map +1 -0
- package/dist/components/ui/expandable-tabs.js +170 -0
- package/dist/components/ui/overlay.cjs +69 -0
- package/dist/components/ui/overlay.d.ts +7 -0
- package/dist/components/ui/overlay.d.ts.map +1 -0
- package/dist/components/ui/overlay.js +28 -0
- package/dist/components/ui/scroll-area.cjs +105 -0
- package/dist/components/ui/scroll-area.d.ts +19 -0
- package/dist/components/ui/scroll-area.d.ts.map +1 -0
- package/dist/components/ui/scroll-area.js +74 -0
- package/dist/components/ui/switch.cjs +71 -0
- package/dist/components/ui/switch.d.ts +11 -0
- package/dist/components/ui/switch.d.ts.map +1 -0
- package/dist/components/ui/switch.js +33 -0
- package/dist/components/ui/tooltip.cjs +75 -0
- package/dist/components/ui/tooltip.d.ts +16 -0
- package/dist/components/ui/tooltip.d.ts.map +1 -0
- package/dist/components/ui/tooltip.js +38 -0
- package/dist/components/wrapper.cjs +197 -0
- package/dist/components/wrapper.d.ts +24 -0
- package/dist/components/wrapper.d.ts.map +1 -0
- package/dist/components/wrapper.js +165 -0
- package/dist/dev-tool.cjs +164 -0
- package/dist/dev-tool.d.ts +14 -0
- package/dist/dev-tool.d.ts.map +1 -0
- package/dist/dev-tool.js +110 -0
- package/dist/index.cjs +46 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/libs/utils.cjs +69 -0
- package/dist/libs/utils.d.ts +3 -0
- package/dist/libs/utils.d.ts.map +1 -0
- package/dist/libs/utils.js +8 -0
- package/dist/router/router.cjs +278 -0
- package/dist/router/router.d.ts +8 -0
- package/dist/router/router.d.ts.map +1 -0
- package/dist/router/router.js +261 -0
- package/package.json +48 -0
- package/rslib.config.ts +28 -0
- package/src/components/error-state.tsx +44 -0
- package/src/components/header.tsx +28 -0
- package/src/components/ui/accordion.tsx +64 -0
- package/src/components/ui/alert.tsx +59 -0
- package/src/components/ui/button.tsx +56 -0
- package/src/components/ui/card.tsx +81 -0
- package/src/components/ui/expandable-tabs.tsx +174 -0
- package/src/components/ui/overlay.tsx +21 -0
- package/src/components/ui/scroll-area.tsx +52 -0
- package/src/components/ui/switch.tsx +28 -0
- package/src/components/ui/tooltip.tsx +32 -0
- package/src/components/wrapper.tsx +103 -0
- package/src/dev-tool.tsx +117 -0
- package/src/index.ts +3 -0
- package/src/libs/utils.ts +6 -0
- package/src/router/router.tsx +164 -0
- package/tsconfig.json +12 -0
package/src/dev-tool.tsx
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import type { NamespaceProps, PrivacyConsentState } from 'c15t';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
type FC,
|
|
6
|
+
createContext,
|
|
7
|
+
useCallback,
|
|
8
|
+
useContext,
|
|
9
|
+
useEffect,
|
|
10
|
+
useState,
|
|
11
|
+
} from 'react';
|
|
12
|
+
import type { StoreApi } from 'zustand/vanilla';
|
|
13
|
+
import { ErrorState } from './components/error-state';
|
|
14
|
+
import { Header } from './components/header';
|
|
15
|
+
import DevToolWrapper from './components/wrapper';
|
|
16
|
+
import { Router } from './router/router';
|
|
17
|
+
|
|
18
|
+
const PrivacyConsentContext = createContext<{
|
|
19
|
+
state: PrivacyConsentState | null;
|
|
20
|
+
store: StoreApi<PrivacyConsentState> | null;
|
|
21
|
+
} | null>(null);
|
|
22
|
+
|
|
23
|
+
export const getStore = () => {
|
|
24
|
+
const context = useContext(PrivacyConsentContext);
|
|
25
|
+
if (context === null) {
|
|
26
|
+
throw new Error(
|
|
27
|
+
'useConsentManagerContext must be used within a ConsentManagerProvider'
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Create a subscription to the store updates
|
|
32
|
+
const [localState, setLocalState] = useState(context.state);
|
|
33
|
+
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
if (!context.store) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Update local state when context state changes
|
|
40
|
+
setLocalState(context.state);
|
|
41
|
+
|
|
42
|
+
// Subscribe to store updates
|
|
43
|
+
const unsubscribe = context.store.subscribe(
|
|
44
|
+
(newState: PrivacyConsentState) => {
|
|
45
|
+
setLocalState(newState);
|
|
46
|
+
}
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
return () => {
|
|
50
|
+
unsubscribe();
|
|
51
|
+
};
|
|
52
|
+
}, [context.store, context.state]);
|
|
53
|
+
|
|
54
|
+
return localState;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export default PrivacyConsentContext;
|
|
58
|
+
|
|
59
|
+
interface ConsentManagerProviderProps extends NamespaceProps {
|
|
60
|
+
position?: 'bottom-right' | 'top-right' | 'bottom-left' | 'top-left';
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export const ConsentManagerDevTool: FC<ConsentManagerProviderProps> = ({
|
|
64
|
+
namespace = 'c15tStore',
|
|
65
|
+
position = 'bottom-right',
|
|
66
|
+
}) => {
|
|
67
|
+
const [state, setState] = useState<PrivacyConsentState | null>(null);
|
|
68
|
+
const [store, setStore] = useState<StoreApi<PrivacyConsentState> | null>(
|
|
69
|
+
null
|
|
70
|
+
);
|
|
71
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
72
|
+
const toggleOpen = useCallback(() => setIsOpen((prev) => !prev), []);
|
|
73
|
+
|
|
74
|
+
useEffect(() => {
|
|
75
|
+
const storeInstance =
|
|
76
|
+
(typeof window !== 'undefined' &&
|
|
77
|
+
(window as Window)[namespace as keyof Window]) ||
|
|
78
|
+
null;
|
|
79
|
+
|
|
80
|
+
if (storeInstance) {
|
|
81
|
+
setStore(storeInstance);
|
|
82
|
+
const currentState = storeInstance.getState() as PrivacyConsentState;
|
|
83
|
+
setState(currentState);
|
|
84
|
+
|
|
85
|
+
// Subscribe to store updates
|
|
86
|
+
const unsubscribe = storeInstance.subscribe(
|
|
87
|
+
(newState: PrivacyConsentState) => {
|
|
88
|
+
setState(newState);
|
|
89
|
+
}
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
return () => {
|
|
93
|
+
unsubscribe();
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
// biome-ignore lint/suspicious/noConsoleLog: needed for dev tools
|
|
97
|
+
// biome-ignore lint/suspicious/noConsole: needed for dev tools
|
|
98
|
+
console.log(`${namespace} is not available on the window object.`);
|
|
99
|
+
}, [namespace]);
|
|
100
|
+
|
|
101
|
+
return (
|
|
102
|
+
<PrivacyConsentContext.Provider value={{ state, store }}>
|
|
103
|
+
<DevToolWrapper
|
|
104
|
+
isOpen={isOpen}
|
|
105
|
+
toggleOpen={toggleOpen}
|
|
106
|
+
position={position}
|
|
107
|
+
>
|
|
108
|
+
<Header onClose={() => setIsOpen(false)} />
|
|
109
|
+
{state ? (
|
|
110
|
+
<Router onClose={() => setIsOpen(false)} />
|
|
111
|
+
) : (
|
|
112
|
+
<ErrorState namespace={namespace} />
|
|
113
|
+
)}
|
|
114
|
+
</DevToolWrapper>
|
|
115
|
+
</PrivacyConsentContext.Provider>
|
|
116
|
+
);
|
|
117
|
+
};
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
Cookie,
|
|
5
|
+
FileText,
|
|
6
|
+
GanttChartSquare,
|
|
7
|
+
RefreshCw,
|
|
8
|
+
ToggleLeft,
|
|
9
|
+
} from 'lucide-react';
|
|
10
|
+
import { motion } from 'motion/react';
|
|
11
|
+
import { useCallback, useState } from 'react';
|
|
12
|
+
|
|
13
|
+
import type { PrivacyConsentState } from 'c15t';
|
|
14
|
+
import { Button } from '../components/ui/button';
|
|
15
|
+
import { ExpandableTabs } from '../components/ui/expandable-tabs';
|
|
16
|
+
import { ScrollArea } from '../components/ui/scroll-area';
|
|
17
|
+
import { getStore } from '../dev-tool';
|
|
18
|
+
import { cn } from '../libs/utils';
|
|
19
|
+
|
|
20
|
+
type TabSection = 'Consents' | 'Compliance' | 'Scripts' | 'Conditional';
|
|
21
|
+
|
|
22
|
+
const tabs = [
|
|
23
|
+
{ title: 'Consents' as const, icon: ToggleLeft },
|
|
24
|
+
{ title: 'Compliance' as const, icon: GanttChartSquare },
|
|
25
|
+
] as const;
|
|
26
|
+
|
|
27
|
+
interface ContentItem {
|
|
28
|
+
title: string;
|
|
29
|
+
status: string;
|
|
30
|
+
details?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface RouterProps {
|
|
34
|
+
onClose: () => void;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function Router({ onClose }: RouterProps) {
|
|
38
|
+
const privacyConsent = getStore() as PrivacyConsentState;
|
|
39
|
+
const { clearAllData, setIsPrivacyDialogOpen, setShowPopup } = privacyConsent;
|
|
40
|
+
|
|
41
|
+
const [activeSection, setActiveSection] = useState<TabSection>('Consents');
|
|
42
|
+
|
|
43
|
+
// Handle tab change locally
|
|
44
|
+
const handleTabChange = useCallback((index: number | null) => {
|
|
45
|
+
if (index !== null) {
|
|
46
|
+
//@ts-expect-error
|
|
47
|
+
setActiveSection(tabs[index].title);
|
|
48
|
+
}
|
|
49
|
+
}, []);
|
|
50
|
+
|
|
51
|
+
// Compute rendering state without conditions
|
|
52
|
+
const renderingState = [
|
|
53
|
+
{ componentName: 'MarketingContent', consentType: 'marketing' as const },
|
|
54
|
+
{ componentName: 'AnalyticsContent', consentType: 'measurement' as const },
|
|
55
|
+
{
|
|
56
|
+
componentName: 'PersonalizationComponent',
|
|
57
|
+
consentType: 'ad_personalization' as const,
|
|
58
|
+
},
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
// Compute content items based on active section
|
|
62
|
+
const contentItems: ContentItem[] =
|
|
63
|
+
activeSection === 'Consents'
|
|
64
|
+
? Object.entries(privacyConsent.consents).map(([name, value]) => ({
|
|
65
|
+
title: name,
|
|
66
|
+
status: value ? 'Enabled' : 'Disabled',
|
|
67
|
+
}))
|
|
68
|
+
: activeSection === 'Compliance'
|
|
69
|
+
? Object.entries(privacyConsent.complianceSettings).map(
|
|
70
|
+
([region, settings]) => ({
|
|
71
|
+
title: region,
|
|
72
|
+
status: settings.enabled ? 'Active' : 'Inactive',
|
|
73
|
+
})
|
|
74
|
+
)
|
|
75
|
+
: activeSection === 'Conditional'
|
|
76
|
+
? renderingState.map((item) => ({
|
|
77
|
+
title: item.componentName,
|
|
78
|
+
status: 'Rendered',
|
|
79
|
+
details: `Requires: ${item.consentType}`,
|
|
80
|
+
}))
|
|
81
|
+
: [];
|
|
82
|
+
|
|
83
|
+
const handleResetConsent = useCallback(() => {
|
|
84
|
+
clearAllData();
|
|
85
|
+
onClose();
|
|
86
|
+
}, [clearAllData, onClose]);
|
|
87
|
+
|
|
88
|
+
const handleOpenPrivacyModal = useCallback(() => {
|
|
89
|
+
setIsPrivacyDialogOpen(true);
|
|
90
|
+
}, [setIsPrivacyDialogOpen]);
|
|
91
|
+
|
|
92
|
+
const handleOpenCookiePopup = useCallback(() => {
|
|
93
|
+
setShowPopup(true);
|
|
94
|
+
}, [setShowPopup]);
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
<>
|
|
98
|
+
<div className="border-b p-4">
|
|
99
|
+
<ExpandableTabs
|
|
100
|
+
tabs={Array.from(tabs)}
|
|
101
|
+
activeColor="text-primary"
|
|
102
|
+
className="border-muted"
|
|
103
|
+
onChange={handleTabChange}
|
|
104
|
+
/>
|
|
105
|
+
</div>
|
|
106
|
+
<ScrollArea className="h-[300px]">
|
|
107
|
+
<motion.div
|
|
108
|
+
className="space-y-2 p-4"
|
|
109
|
+
initial={{ opacity: 0 }}
|
|
110
|
+
animate={{ opacity: 1 }}
|
|
111
|
+
exit={{ opacity: 0 }}
|
|
112
|
+
>
|
|
113
|
+
{contentItems.map((item, index) => (
|
|
114
|
+
<motion.div
|
|
115
|
+
key={`${activeSection}-${item.title}`}
|
|
116
|
+
className="flex items-center justify-between rounded-lg border bg-card p-3"
|
|
117
|
+
initial={{ opacity: 0, y: 20 }}
|
|
118
|
+
animate={{ opacity: 1, y: 0 }}
|
|
119
|
+
transition={{ delay: index * 0.05 }}
|
|
120
|
+
>
|
|
121
|
+
<div className="flex flex-col">
|
|
122
|
+
<span className="font-medium text-sm">{item.title}</span>
|
|
123
|
+
{item.details && (
|
|
124
|
+
<span className="text-muted-foreground text-xs">
|
|
125
|
+
{item.details}
|
|
126
|
+
</span>
|
|
127
|
+
)}
|
|
128
|
+
</div>
|
|
129
|
+
<span
|
|
130
|
+
className={cn(
|
|
131
|
+
'rounded-full px-2 py-1 text-xs',
|
|
132
|
+
item.status === 'Enabled' ||
|
|
133
|
+
item.status === 'Active' ||
|
|
134
|
+
item.status === 'active' ||
|
|
135
|
+
item.status === 'Rendered'
|
|
136
|
+
? 'bg-green-100 text-green-800'
|
|
137
|
+
: 'bg-red-100 text-red-800'
|
|
138
|
+
)}
|
|
139
|
+
>
|
|
140
|
+
{item.status}
|
|
141
|
+
</span>
|
|
142
|
+
</motion.div>
|
|
143
|
+
))}
|
|
144
|
+
</motion.div>
|
|
145
|
+
</ScrollArea>
|
|
146
|
+
<div className="border-t p-4">
|
|
147
|
+
<div className="flex flex-col gap-2">
|
|
148
|
+
<Button variant="outline" size="sm" onClick={handleResetConsent}>
|
|
149
|
+
<RefreshCw className="mr-2 h-4 w-4" />
|
|
150
|
+
Reset Local Storage Consent
|
|
151
|
+
</Button>
|
|
152
|
+
<Button variant="outline" size="sm" onClick={handleOpenPrivacyModal}>
|
|
153
|
+
<FileText className="mr-2 h-4 w-4" />
|
|
154
|
+
Open Privacy Settings
|
|
155
|
+
</Button>
|
|
156
|
+
<Button variant="outline" size="sm" onClick={handleOpenCookiePopup}>
|
|
157
|
+
<Cookie className="mr-2 h-4 w-4" />
|
|
158
|
+
Open Cookie Popup
|
|
159
|
+
</Button>
|
|
160
|
+
</div>
|
|
161
|
+
</div>
|
|
162
|
+
</>
|
|
163
|
+
);
|
|
164
|
+
}
|
package/tsconfig.json
ADDED