@dyrected/admin 1.0.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.
- package/CHANGELOG.md +40 -0
- package/LICENSE.md +50 -0
- package/README.md +73 -0
- package/components.json +17 -0
- package/eslint.config.js +22 -0
- package/index.html +13 -0
- package/package.json +99 -0
- package/postcss.config.js +6 -0
- package/public/favicon.svg +1 -0
- package/public/icons.svg +24 -0
- package/src/App.css +184 -0
- package/src/App.tsx +25 -0
- package/src/assets/dyrected.svg +155 -0
- package/src/assets/hero.png +0 -0
- package/src/assets/react.svg +1 -0
- package/src/assets/vite.svg +1 -0
- package/src/components/auth/auth-gate.tsx +64 -0
- package/src/components/error-boundary.tsx +45 -0
- package/src/components/forms/field-renderer.tsx +111 -0
- package/src/components/forms/fields/block-builder.tsx +213 -0
- package/src/components/forms/fields/date-picker.tsx +60 -0
- package/src/components/forms/fields/json-editor.tsx +62 -0
- package/src/components/forms/fields/media-picker.tsx +286 -0
- package/src/components/forms/fields/multi-select.tsx +145 -0
- package/src/components/forms/fields/radio-field.tsx +51 -0
- package/src/components/forms/fields/relationship-picker.tsx +143 -0
- package/src/components/forms/fields/rich-text-editor.tsx +224 -0
- package/src/components/forms/fields/select-field.tsx +35 -0
- package/src/components/forms/fields/switch-field.tsx +16 -0
- package/src/components/forms/fields/text-area-field.tsx +15 -0
- package/src/components/forms/fields/text-field.tsx +24 -0
- package/src/components/forms/form-engine.tsx +87 -0
- package/src/components/forms/form-field-renderer.tsx +269 -0
- package/src/components/forms/utils.ts +97 -0
- package/src/components/layout/admin-shell.tsx +479 -0
- package/src/components/layout/branding-provider.tsx +112 -0
- package/src/components/live-preview/LivePreviewPane.tsx +128 -0
- package/src/components/media/focal-point-picker.tsx +66 -0
- package/src/components/media/media-card.tsx +44 -0
- package/src/components/media/media-grid.tsx +32 -0
- package/src/components/media/media-library-dialog.tsx +465 -0
- package/src/components/ui/aspect-ratio.tsx +7 -0
- package/src/components/ui/badge.tsx +36 -0
- package/src/components/ui/button.tsx +56 -0
- package/src/components/ui/calendar.tsx +214 -0
- package/src/components/ui/card.tsx +79 -0
- package/src/components/ui/checkbox.tsx +28 -0
- package/src/components/ui/command.tsx +151 -0
- package/src/components/ui/data-table.tsx +219 -0
- package/src/components/ui/dialog.tsx +122 -0
- package/src/components/ui/dropdown-menu.tsx +200 -0
- package/src/components/ui/form.tsx +178 -0
- package/src/components/ui/input.tsx +24 -0
- package/src/components/ui/label.tsx +24 -0
- package/src/components/ui/page-header.tsx +30 -0
- package/src/components/ui/pagination.tsx +57 -0
- package/src/components/ui/popover.tsx +29 -0
- package/src/components/ui/progress.tsx +26 -0
- package/src/components/ui/radio-group.tsx +42 -0
- package/src/components/ui/render-cell.tsx +110 -0
- package/src/components/ui/scroll-area.tsx +46 -0
- package/src/components/ui/select.tsx +160 -0
- package/src/components/ui/separator.tsx +29 -0
- package/src/components/ui/sheet.tsx +140 -0
- package/src/components/ui/sidebar.tsx +771 -0
- package/src/components/ui/skeleton.tsx +15 -0
- package/src/components/ui/sonner.tsx +27 -0
- package/src/components/ui/switch.tsx +27 -0
- package/src/components/ui/table.tsx +117 -0
- package/src/components/ui/tabs.tsx +53 -0
- package/src/components/ui/textarea.tsx +22 -0
- package/src/components/ui/toggle.tsx +43 -0
- package/src/components/ui/tooltip.tsx +28 -0
- package/src/hooks/use-mobile.tsx +19 -0
- package/src/hooks/use-preferences.ts +56 -0
- package/src/index.css +111 -0
- package/src/index.tsx +198 -0
- package/src/lib/utils.ts +32 -0
- package/src/main.tsx +10 -0
- package/src/pages/auth/first-user-page.tsx +115 -0
- package/src/pages/auth/login-page.tsx +91 -0
- package/src/pages/collections/edit-page.tsx +280 -0
- package/src/pages/collections/list-page.tsx +343 -0
- package/src/pages/dashboard/dashboard.tsx +150 -0
- package/src/pages/globals/editor-page.tsx +122 -0
- package/src/pages/media/media-page.tsx +564 -0
- package/src/pages/setup/setup-prompt.tsx +152 -0
- package/src/providers/dyrected-provider.tsx +122 -0
- package/src/providers/query-provider.tsx +19 -0
- package/src/types/jexl.d.ts +11 -0
- package/tailwind.config.ts +102 -0
- package/tsconfig.app.json +29 -0
- package/tsconfig.json +12 -0
- package/tsconfig.node.json +27 -0
- package/vite.config.ts +36 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
import {
|
|
3
|
+
Copy,
|
|
4
|
+
Check,
|
|
5
|
+
ExternalLink,
|
|
6
|
+
Terminal,
|
|
7
|
+
Sparkles,
|
|
8
|
+
ShieldCheck,
|
|
9
|
+
} from "lucide-react";
|
|
10
|
+
import { cn } from "../../lib/utils";
|
|
11
|
+
import { Button } from "../../components/ui/button";
|
|
12
|
+
import { generateAIPrompt, type SetupPromptConfig } from "@dyrected/core";
|
|
13
|
+
|
|
14
|
+
export type { SetupPromptConfig };
|
|
15
|
+
|
|
16
|
+
export interface SetupPromptProps {
|
|
17
|
+
config: SetupPromptConfig;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function SetupPromptUI({ config }: SetupPromptProps) {
|
|
21
|
+
const [copied, setCopied] = useState<string | null>(null);
|
|
22
|
+
const [activeTab, setActiveTab] = useState<"next" | "nuxt" | "react" | "vue">("next");
|
|
23
|
+
|
|
24
|
+
const promptText = generateAIPrompt(activeTab, config);
|
|
25
|
+
|
|
26
|
+
const copyToClipboard = (text: string, id: string) => {
|
|
27
|
+
navigator.clipboard.writeText(text);
|
|
28
|
+
setCopied(id);
|
|
29
|
+
setTimeout(() => setCopied(null), 2000);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<div className="max-w-4xl mx-auto space-y-8 py-8 animate-in slide-in-from-bottom-4 duration-700">
|
|
34
|
+
<div className="text-center space-y-4">
|
|
35
|
+
<div className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-primary/10 text-primary text-xs font-bold uppercase tracking-wider mb-2">
|
|
36
|
+
<Sparkles className="h-3 w-3" />
|
|
37
|
+
Integration Guide
|
|
38
|
+
</div>
|
|
39
|
+
<h1 className="text-2xl font-semibold tracking-tight lg:text-5xl text-foreground">
|
|
40
|
+
Connect Your Application
|
|
41
|
+
</h1>
|
|
42
|
+
<p className="text-lg text-muted-foreground max-w-2xl mx-auto">
|
|
43
|
+
Use the AI prompt below to set up your frontend automatically, or follow the steps manually.
|
|
44
|
+
</p>
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
<div className="grid gap-6">
|
|
48
|
+
{/* Site Credentials */}
|
|
49
|
+
<section className="rounded-2xl border bg-card overflow-hidden shadow-xl">
|
|
50
|
+
<div className="p-4 border-b bg-muted/30">
|
|
51
|
+
<h3 className="text-lg font-semibold flex items-center gap-2">
|
|
52
|
+
<ShieldCheck className="h-5 w-5 text-primary" />
|
|
53
|
+
Site Credentials
|
|
54
|
+
</h3>
|
|
55
|
+
</div>
|
|
56
|
+
<div className="p-4">
|
|
57
|
+
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
|
58
|
+
{[
|
|
59
|
+
{ label: "Site ID", value: config.siteId, id: "siteId" },
|
|
60
|
+
{ label: "API Key", value: config.apiKey, id: "apiKey" },
|
|
61
|
+
{ label: "Base URL", value: config.baseUrl, id: "baseUrl" },
|
|
62
|
+
].map((item) => (
|
|
63
|
+
<div key={item.id} className="space-y-2">
|
|
64
|
+
<label className="text-xs font-bold text-muted-foreground uppercase">
|
|
65
|
+
{item.label}
|
|
66
|
+
</label>
|
|
67
|
+
<div className="relative group">
|
|
68
|
+
<div className="p-3 pr-10 rounded-lg bg-muted text-sm font-mono truncate border border-transparent group-hover:border-primary/20 transition-all">
|
|
69
|
+
{item.value}
|
|
70
|
+
</div>
|
|
71
|
+
<button
|
|
72
|
+
onClick={() => copyToClipboard(item.value || "", item.id)}
|
|
73
|
+
className="absolute right-2 top-1/2 -translate-y-1/2 p-1.5 rounded-md hover:bg-background transition-colors text-muted-foreground"
|
|
74
|
+
>
|
|
75
|
+
{copied === item.id ? (
|
|
76
|
+
<Check className="h-4 w-4 text-green-500" />
|
|
77
|
+
) : (
|
|
78
|
+
<Copy className="h-4 w-4" />
|
|
79
|
+
)}
|
|
80
|
+
</button>
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
))}
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
</section>
|
|
87
|
+
|
|
88
|
+
{/* AI Prompt */}
|
|
89
|
+
<section className="rounded-2xl border bg-white overflow-hidden shadow-xl ring-1 ring-primary/20">
|
|
90
|
+
<div className="p-6 border-b bg-primary/5 flex items-center justify-between gap-4 flex-wrap">
|
|
91
|
+
<div className="space-y-3">
|
|
92
|
+
<h3 className="text-lg font-semibold flex items-center gap-2">
|
|
93
|
+
<Sparkles className="h-5 w-5 text-primary animate-pulse" />
|
|
94
|
+
AI Integration Prompt
|
|
95
|
+
</h3>
|
|
96
|
+
<div className="flex gap-2 bg-muted/50 p-1 rounded-lg w-fit">
|
|
97
|
+
{(["next", "nuxt", "react", "vue"] as const).map((tab) => (
|
|
98
|
+
<button
|
|
99
|
+
key={tab}
|
|
100
|
+
onClick={() => setActiveTab(tab)}
|
|
101
|
+
className={cn(
|
|
102
|
+
"px-4 py-1.5 rounded-md text-xs font-medium transition-all capitalize",
|
|
103
|
+
activeTab === tab
|
|
104
|
+
? "bg-background text-foreground shadow-sm"
|
|
105
|
+
: "text-muted-foreground hover:text-foreground"
|
|
106
|
+
)}
|
|
107
|
+
>
|
|
108
|
+
{tab === "next" ? "Next.js" : tab === "nuxt" ? "Nuxt" : tab}
|
|
109
|
+
</button>
|
|
110
|
+
))}
|
|
111
|
+
</div>
|
|
112
|
+
<p className="text-sm text-muted-foreground">
|
|
113
|
+
Copy and paste this into your AI developer to handle everything automatically
|
|
114
|
+
</p>
|
|
115
|
+
</div>
|
|
116
|
+
<Button
|
|
117
|
+
onClick={() => copyToClipboard(promptText, "ai-developer")}
|
|
118
|
+
className="relative overflow-hidden group shrink-0"
|
|
119
|
+
>
|
|
120
|
+
<div className="flex items-center gap-2">
|
|
121
|
+
{copied === "ai-developer" ? (
|
|
122
|
+
<Check className="h-4 w-4" />
|
|
123
|
+
) : (
|
|
124
|
+
<Copy className="h-4 w-4" />
|
|
125
|
+
)}
|
|
126
|
+
{copied === "ai-developer" ? "Copied!" : "Copy Full Prompt"}
|
|
127
|
+
</div>
|
|
128
|
+
</Button>
|
|
129
|
+
</div>
|
|
130
|
+
<div className="p-6 bg-slate-950 text-slate-300 font-mono text-xs leading-relaxed max-h-[400px] overflow-y-auto scrollbar-thin scrollbar-thumb-white/10">
|
|
131
|
+
<pre className="whitespace-pre-wrap">{promptText}</pre>
|
|
132
|
+
</div>
|
|
133
|
+
</section>
|
|
134
|
+
</div>
|
|
135
|
+
|
|
136
|
+
<div className="flex justify-center gap-4 pt-4">
|
|
137
|
+
<Button variant="outline" asChild>
|
|
138
|
+
<a
|
|
139
|
+
href={`${config.baseUrl}/api/docs`}
|
|
140
|
+
target="_blank"
|
|
141
|
+
rel="noopener noreferrer"
|
|
142
|
+
className="flex items-center gap-2"
|
|
143
|
+
>
|
|
144
|
+
<Terminal className="h-4 w-4" />
|
|
145
|
+
API Documentation
|
|
146
|
+
<ExternalLink className="h-3 w-3" />
|
|
147
|
+
</a>
|
|
148
|
+
</Button>
|
|
149
|
+
</div>
|
|
150
|
+
</div>
|
|
151
|
+
);
|
|
152
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import React, { createContext, useContext, useState, useEffect } from "react";
|
|
2
|
+
import { createClient, DyrectedClient } from "@dyrected/sdk";
|
|
3
|
+
|
|
4
|
+
interface DyrectedContextType {
|
|
5
|
+
client: DyrectedClient | null;
|
|
6
|
+
config: {
|
|
7
|
+
baseUrl: string;
|
|
8
|
+
apiKey: string | undefined;
|
|
9
|
+
siteId: string | undefined;
|
|
10
|
+
};
|
|
11
|
+
setAuth: (baseUrl: string, apiKey: string, siteId?: string) => void;
|
|
12
|
+
logout: () => void;
|
|
13
|
+
isAuthenticated: boolean;
|
|
14
|
+
schemas: { collections: any[]; globals: any[]; admin?: any } | null;
|
|
15
|
+
user: any | null;
|
|
16
|
+
setToken: (token: string) => void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const DyrectedContext = createContext<DyrectedContextType | undefined>(undefined);
|
|
20
|
+
|
|
21
|
+
export interface DyrectedProviderProps {
|
|
22
|
+
children: React.ReactNode;
|
|
23
|
+
apiKey?: string;
|
|
24
|
+
baseUrl?: string;
|
|
25
|
+
siteId?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function DyrectedProvider({
|
|
29
|
+
children,
|
|
30
|
+
apiKey: initialApiKey,
|
|
31
|
+
baseUrl: initialBaseUrl,
|
|
32
|
+
siteId: initialSiteId
|
|
33
|
+
}: DyrectedProviderProps) {
|
|
34
|
+
const [baseUrl, setBaseUrl] = useState<string>(() => initialBaseUrl || (typeof window !== 'undefined' ? localStorage.getItem("dyrected_url") : null) || "");
|
|
35
|
+
const [apiKey, setApiKey] = useState<string | undefined>(() => initialApiKey || (typeof window !== 'undefined' ? localStorage.getItem("dyrected_key") : null) || undefined);
|
|
36
|
+
const [siteId, setSiteId] = useState<string | undefined>(() => initialSiteId || (typeof window !== 'undefined' ? localStorage.getItem("dyrected_site_id") : null) || undefined);
|
|
37
|
+
const [client, setClient] = useState<DyrectedClient | null>(null);
|
|
38
|
+
const [schemas, setSchemas] = useState<{ collections: any[]; globals: any[]; admin?: any } | null>(null);
|
|
39
|
+
const [user, setUser] = useState<any | null>(null);
|
|
40
|
+
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
if (baseUrl) {
|
|
43
|
+
const newClient = createClient({
|
|
44
|
+
baseUrl,
|
|
45
|
+
apiKey: apiKey || undefined,
|
|
46
|
+
siteId: siteId || undefined,
|
|
47
|
+
});
|
|
48
|
+
setClient(newClient);
|
|
49
|
+
|
|
50
|
+
// Fetch schemas
|
|
51
|
+
newClient.getSchemas().then(setSchemas).catch(err => {
|
|
52
|
+
console.error("Failed to fetch schemas:", err);
|
|
53
|
+
setSchemas(null);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}, [baseUrl, apiKey, siteId]);
|
|
57
|
+
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
const token = localStorage.getItem("dyrected_token");
|
|
60
|
+
if (token && client && schemas && !user) {
|
|
61
|
+
setToken(token);
|
|
62
|
+
}
|
|
63
|
+
}, [client, schemas, user]);
|
|
64
|
+
|
|
65
|
+
const setAuth = (newUrl: string, newKey: string, newSiteId?: string) => {
|
|
66
|
+
localStorage.setItem("dyrected_url", newUrl);
|
|
67
|
+
localStorage.setItem("dyrected_key", newKey);
|
|
68
|
+
if (newSiteId) localStorage.setItem("dyrected_site_id", newSiteId);
|
|
69
|
+
else localStorage.removeItem("dyrected_site_id");
|
|
70
|
+
|
|
71
|
+
setBaseUrl(newUrl);
|
|
72
|
+
setApiKey(newKey);
|
|
73
|
+
setSiteId(newSiteId);
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const setToken = (token: string) => {
|
|
77
|
+
localStorage.setItem("dyrected_token", token);
|
|
78
|
+
if (client) {
|
|
79
|
+
client.setToken(token);
|
|
80
|
+
// Prefer __admins for the dashboard session; fall back to the first auth collection.
|
|
81
|
+
const authCollection =
|
|
82
|
+
schemas?.collections.find((c: any) => c.slug === '__admins') ??
|
|
83
|
+
schemas?.collections.find((c: any) => c.auth);
|
|
84
|
+
if (authCollection) {
|
|
85
|
+
client.collection(authCollection.slug).me().then(setUser).catch(() => setUser(null));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const logout = () => {
|
|
91
|
+
localStorage.removeItem("dyrected_url");
|
|
92
|
+
localStorage.removeItem("dyrected_key");
|
|
93
|
+
localStorage.removeItem("dyrected_site_id");
|
|
94
|
+
localStorage.removeItem("dyrected_token");
|
|
95
|
+
setBaseUrl("");
|
|
96
|
+
setApiKey(undefined);
|
|
97
|
+
setSiteId(undefined);
|
|
98
|
+
setClient(null);
|
|
99
|
+
setUser(null);
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
return (
|
|
103
|
+
<DyrectedContext.Provider value={{
|
|
104
|
+
client,
|
|
105
|
+
config: { baseUrl, apiKey, siteId },
|
|
106
|
+
setAuth,
|
|
107
|
+
setToken,
|
|
108
|
+
logout,
|
|
109
|
+
isAuthenticated: !!baseUrl && !!apiKey,
|
|
110
|
+
schemas,
|
|
111
|
+
user
|
|
112
|
+
}}>
|
|
113
|
+
{children}
|
|
114
|
+
</DyrectedContext.Provider>
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export const useDyrected = () => {
|
|
119
|
+
const context = useContext(DyrectedContext);
|
|
120
|
+
if (!context) throw new Error("useDyrected must be used within a DyrectedProvider");
|
|
121
|
+
return context;
|
|
122
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
|
|
4
|
+
export function QueryProvider({ children }: { children: React.ReactNode }) {
|
|
5
|
+
const [queryClient] = useState(() => new QueryClient({
|
|
6
|
+
defaultOptions: {
|
|
7
|
+
queries: {
|
|
8
|
+
staleTime: 60 * 1000,
|
|
9
|
+
retry: 1,
|
|
10
|
+
},
|
|
11
|
+
},
|
|
12
|
+
}));
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<QueryClientProvider client={queryClient}>
|
|
16
|
+
{children}
|
|
17
|
+
</QueryClientProvider>
|
|
18
|
+
);
|
|
19
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
declare module 'jexl' {
|
|
2
|
+
const jexl: {
|
|
3
|
+
evalSync: (expression: string, context: any) => any;
|
|
4
|
+
eval: (expression: string, context: any) => Promise<any>;
|
|
5
|
+
addFunction: (name: string, fn: (...args: any[]) => any) => void;
|
|
6
|
+
addTransform: (name: string, fn: (...args: any[]) => any) => void;
|
|
7
|
+
addBinaryOp: (name: string, precedence: number, fn: (a: any, b: any) => any) => void;
|
|
8
|
+
addUnaryOp: (name: string, fn: (a: any) => any) => void;
|
|
9
|
+
};
|
|
10
|
+
export default jexl;
|
|
11
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import type { Config } from "tailwindcss"
|
|
2
|
+
import tailwindAnimate from "tailwindcss-animate"
|
|
3
|
+
|
|
4
|
+
const config: Config = {
|
|
5
|
+
darkMode: ["class"],
|
|
6
|
+
content: [
|
|
7
|
+
'./pages/**/*.{ts,tsx}',
|
|
8
|
+
'./components/**/*.{ts,tsx}',
|
|
9
|
+
'./app/**/*.{ts,tsx}',
|
|
10
|
+
'./src/**/*.{ts,tsx}',
|
|
11
|
+
],
|
|
12
|
+
theme: {
|
|
13
|
+
container: {
|
|
14
|
+
center: true,
|
|
15
|
+
padding: '2rem',
|
|
16
|
+
screens: {
|
|
17
|
+
'2xl': '1400px'
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
extend: {
|
|
21
|
+
fontFamily: {
|
|
22
|
+
sans: ['var(--font-sans)', 'ui-sans-serif', 'system-ui', 'sans-serif'],
|
|
23
|
+
serif: ['var(--font-serif)', 'ui-serif', 'Georgia', 'serif'],
|
|
24
|
+
},
|
|
25
|
+
colors: {
|
|
26
|
+
border: 'hsl(var(--border))',
|
|
27
|
+
input: 'hsl(var(--input))',
|
|
28
|
+
ring: 'hsl(var(--ring))',
|
|
29
|
+
background: 'hsl(var(--background))',
|
|
30
|
+
foreground: 'hsl(var(--foreground))',
|
|
31
|
+
primary: {
|
|
32
|
+
DEFAULT: 'hsl(var(--primary))',
|
|
33
|
+
foreground: 'hsl(var(--primary-foreground))'
|
|
34
|
+
},
|
|
35
|
+
secondary: {
|
|
36
|
+
DEFAULT: 'hsl(var(--secondary))',
|
|
37
|
+
foreground: 'hsl(var(--secondary-foreground))'
|
|
38
|
+
},
|
|
39
|
+
destructive: {
|
|
40
|
+
DEFAULT: 'hsl(var(--destructive))',
|
|
41
|
+
foreground: 'hsl(var(--destructive-foreground))'
|
|
42
|
+
},
|
|
43
|
+
muted: {
|
|
44
|
+
DEFAULT: 'hsl(var(--muted))',
|
|
45
|
+
foreground: 'hsl(var(--muted-foreground))'
|
|
46
|
+
},
|
|
47
|
+
accent: {
|
|
48
|
+
DEFAULT: 'hsl(var(--accent))',
|
|
49
|
+
foreground: 'hsl(var(--accent-foreground))'
|
|
50
|
+
},
|
|
51
|
+
popover: {
|
|
52
|
+
DEFAULT: 'hsl(var(--popover))',
|
|
53
|
+
foreground: 'hsl(var(--popover-foreground))'
|
|
54
|
+
},
|
|
55
|
+
card: {
|
|
56
|
+
DEFAULT: 'hsl(var(--card))',
|
|
57
|
+
foreground: 'hsl(var(--card-foreground))'
|
|
58
|
+
},
|
|
59
|
+
sidebar: {
|
|
60
|
+
DEFAULT: 'hsl(var(--sidebar-background))',
|
|
61
|
+
foreground: 'hsl(var(--sidebar-foreground))',
|
|
62
|
+
primary: 'hsl(var(--sidebar-primary))',
|
|
63
|
+
'primary-foreground': 'hsl(var(--sidebar-primary-foreground))',
|
|
64
|
+
accent: 'hsl(var(--sidebar-accent))',
|
|
65
|
+
'accent-foreground': 'hsl(var(--sidebar-accent-foreground))',
|
|
66
|
+
border: 'hsl(var(--sidebar-border))',
|
|
67
|
+
ring: 'hsl(var(--sidebar-ring))'
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
borderRadius: {
|
|
71
|
+
lg: 'var(--radius)',
|
|
72
|
+
md: 'calc(var(--radius) - 2px)',
|
|
73
|
+
sm: 'calc(var(--radius) - 4px)'
|
|
74
|
+
},
|
|
75
|
+
keyframes: {
|
|
76
|
+
'accordion-down': {
|
|
77
|
+
from: {
|
|
78
|
+
height: '0'
|
|
79
|
+
},
|
|
80
|
+
to: {
|
|
81
|
+
height: 'var(--radix-accordion-content-height)'
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
'accordion-up': {
|
|
85
|
+
from: {
|
|
86
|
+
height: 'var(--radix-accordion-content-height)'
|
|
87
|
+
},
|
|
88
|
+
to: {
|
|
89
|
+
height: '0'
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
animation: {
|
|
94
|
+
'accordion-down': 'accordion-down 0.2s ease-out',
|
|
95
|
+
'accordion-up': 'accordion-up 0.2s ease-out'
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
plugins: [tailwindAnimate],
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export default config
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
|
4
|
+
"target": "es2023",
|
|
5
|
+
"lib": ["ES2023", "DOM"],
|
|
6
|
+
"module": "esnext",
|
|
7
|
+
"types": ["vite/client"],
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"paths": {
|
|
10
|
+
"@/*": ["./src/*"],
|
|
11
|
+
"@dyrected/core": ["../core/src/index.ts"]
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
/* Bundler mode */
|
|
15
|
+
"moduleResolution": "bundler",
|
|
16
|
+
"allowImportingTsExtensions": true,
|
|
17
|
+
"verbatimModuleSyntax": true,
|
|
18
|
+
"moduleDetection": "force",
|
|
19
|
+
"noEmit": true,
|
|
20
|
+
"jsx": "react-jsx",
|
|
21
|
+
|
|
22
|
+
/* Linting */
|
|
23
|
+
"noUnusedLocals": true,
|
|
24
|
+
"noUnusedParameters": true,
|
|
25
|
+
"erasableSyntaxOnly": true,
|
|
26
|
+
"noFallthroughCasesInSwitch": true
|
|
27
|
+
},
|
|
28
|
+
"include": ["src"]
|
|
29
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
|
4
|
+
"target": "es2023",
|
|
5
|
+
"lib": ["ES2023"],
|
|
6
|
+
"module": "esnext",
|
|
7
|
+
"types": ["node"],
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"paths": {
|
|
10
|
+
"@dyrected/core": ["../core/src/index.ts"]
|
|
11
|
+
},
|
|
12
|
+
|
|
13
|
+
/* Bundler mode */
|
|
14
|
+
"moduleResolution": "bundler",
|
|
15
|
+
"allowImportingTsExtensions": true,
|
|
16
|
+
"verbatimModuleSyntax": true,
|
|
17
|
+
"moduleDetection": "force",
|
|
18
|
+
"noEmit": true,
|
|
19
|
+
|
|
20
|
+
/* Linting */
|
|
21
|
+
"noUnusedLocals": true,
|
|
22
|
+
"noUnusedParameters": true,
|
|
23
|
+
"erasableSyntaxOnly": true,
|
|
24
|
+
"noFallthroughCasesInSwitch": true
|
|
25
|
+
},
|
|
26
|
+
"include": ["vite.config.ts"]
|
|
27
|
+
}
|
package/vite.config.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import { defineConfig } from "vite";
|
|
3
|
+
import react from "@vitejs/plugin-react";
|
|
4
|
+
import { nodePolyfills } from "vite-plugin-node-polyfills";
|
|
5
|
+
|
|
6
|
+
// https://vite.dev/config/
|
|
7
|
+
export default defineConfig({
|
|
8
|
+
plugins: [
|
|
9
|
+
react(),
|
|
10
|
+
nodePolyfills({
|
|
11
|
+
// Whether to polyfill `node:` protocol imports.
|
|
12
|
+
protocolImports: true,
|
|
13
|
+
}),
|
|
14
|
+
],
|
|
15
|
+
build: {
|
|
16
|
+
lib: {
|
|
17
|
+
entry: path.resolve(__dirname, "src/index.tsx"),
|
|
18
|
+
name: "DyrectedAdmin",
|
|
19
|
+
fileName: () => "index.mjs",
|
|
20
|
+
formats: ["es"],
|
|
21
|
+
},
|
|
22
|
+
rollupOptions: {
|
|
23
|
+
output: {
|
|
24
|
+
format: "es",
|
|
25
|
+
manualChunks: undefined,
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
resolve: {
|
|
30
|
+
alias: {
|
|
31
|
+
"@": path.resolve(__dirname, "./src"),
|
|
32
|
+
"@dyrected/core": path.resolve(__dirname, "../core/src"),
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
|