cinqcinqdev-seo 0.1.8 → 0.1.10
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/module.d.mts +17 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +19 -11
- package/dist/runtime/assets/admin-tw.css +1 -1
- package/dist/runtime/components/admin/ContentCalendar.d.vue.ts +3 -0
- package/dist/runtime/components/admin/ContentCalendar.vue +196 -0
- package/dist/runtime/components/admin/ContentCalendar.vue.d.ts +3 -0
- package/dist/runtime/components/admin/DashboardLayout.d.vue.ts +13 -0
- package/dist/runtime/components/admin/DashboardLayout.vue +12 -0
- package/dist/runtime/components/admin/DashboardLayout.vue.d.ts +13 -0
- package/dist/runtime/components/admin/NavItem.d.vue.ts +3 -0
- package/dist/runtime/components/admin/NavItem.vue +46 -0
- package/dist/runtime/components/admin/NavItem.vue.d.ts +3 -0
- package/dist/runtime/components/admin/Navbar.vue +1 -1
- package/dist/runtime/components/admin/Sidebar.d.vue.ts +3 -0
- package/dist/runtime/components/admin/Sidebar.vue +115 -0
- package/dist/runtime/components/admin/Sidebar.vue.d.ts +3 -0
- package/dist/runtime/composables/useAdminBranding.d.ts +4 -0
- package/dist/runtime/composables/useAdminBranding.js +22 -0
- package/dist/runtime/composables/useAdminContent.d.ts +9 -0
- package/dist/runtime/composables/useAdminContent.js +46 -0
- package/dist/runtime/composables/useAdminPersonas.d.ts +8 -0
- package/dist/runtime/composables/useAdminPersonas.js +31 -0
- package/dist/runtime/composables/useAdminProducts.d.ts +6 -0
- package/dist/runtime/composables/useAdminProducts.js +24 -0
- package/dist/runtime/composables/useAdminSections.js +1 -0
- package/dist/runtime/composables/useAdminStrategies.d.ts +10 -0
- package/dist/runtime/composables/useAdminStrategies.js +57 -0
- package/dist/runtime/composables/useAdminTodos.d.ts +10 -0
- package/dist/runtime/composables/useAdminTodos.js +55 -0
- package/dist/runtime/middleware/admin-auth.js +2 -0
- package/dist/runtime/pages/admin/account.vue +1 -0
- package/dist/runtime/pages/admin/branding.d.vue.ts +3 -0
- package/dist/runtime/pages/admin/branding.vue +276 -0
- package/dist/runtime/pages/admin/branding.vue.d.ts +3 -0
- package/dist/runtime/pages/admin/calendar.d.vue.ts +3 -0
- package/dist/runtime/pages/admin/calendar.vue +169 -0
- package/dist/runtime/pages/admin/calendar.vue.d.ts +3 -0
- package/dist/runtime/pages/admin/content-library.d.vue.ts +3 -0
- package/dist/runtime/pages/admin/content-library.vue +359 -0
- package/dist/runtime/pages/admin/content-library.vue.d.ts +3 -0
- package/dist/runtime/pages/admin/editor/[id].vue +5 -2
- package/dist/runtime/pages/admin/index.vue +81 -22
- package/dist/runtime/pages/admin/pages/[type].vue +1 -0
- package/dist/runtime/pages/admin/personas/[id].d.vue.ts +3 -0
- package/dist/runtime/pages/admin/personas/[id].vue +170 -0
- package/dist/runtime/pages/admin/personas/[id].vue.d.ts +3 -0
- package/dist/runtime/pages/admin/personas/index.d.vue.ts +3 -0
- package/dist/runtime/pages/admin/personas/index.vue +266 -0
- package/dist/runtime/pages/admin/personas/index.vue.d.ts +3 -0
- package/dist/runtime/pages/admin/products.d.vue.ts +3 -0
- package/dist/runtime/pages/admin/products.vue +300 -0
- package/dist/runtime/pages/admin/products.vue.d.ts +3 -0
- package/dist/runtime/pages/admin/strategies/[id].d.vue.ts +3 -0
- package/dist/runtime/pages/admin/strategies/[id].vue +237 -0
- package/dist/runtime/pages/admin/strategies/[id].vue.d.ts +3 -0
- package/dist/runtime/pages/admin/strategies/index.d.vue.ts +3 -0
- package/dist/runtime/pages/admin/strategies/index.vue +232 -0
- package/dist/runtime/pages/admin/strategies/index.vue.d.ts +3 -0
- package/dist/runtime/pages/admin/todos.d.vue.ts +3 -0
- package/dist/runtime/pages/admin/todos.vue +251 -0
- package/dist/runtime/pages/admin/todos.vue.d.ts +3 -0
- package/dist/runtime/plugins/admin-font.client.d.ts +0 -5
- package/dist/runtime/plugins/admin-font.client.js +1 -0
- package/dist/runtime/stores/adminUser.d.ts +29 -1
- package/dist/runtime/stores/adminUser.js +4 -2
- package/dist/types.d.mts +1 -1
- package/package.json +6 -3
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<aside data-admin-cms class="w-[220px] bg-[#0d0d0d] flex-shrink-0 flex flex-col overflow-y-auto">
|
|
3
|
+
|
|
4
|
+
<!-- Logo -->
|
|
5
|
+
<div class="px-5 pt-6 pb-5">
|
|
6
|
+
<NuxtLink to="/admin" class="flex items-center gap-2.5">
|
|
7
|
+
<img v-if="branding.logoUrl" :src="branding.logoUrl" class="h-8 w-auto" :alt="branding.name" />
|
|
8
|
+
<span v-else class="w-8 h-8 bg-[#3d35ff] rounded-lg flex items-center justify-center text-white text-[10px] font-black tracking-tight shrink-0">
|
|
9
|
+
{{ brandInitials }}
|
|
10
|
+
</span>
|
|
11
|
+
<div>
|
|
12
|
+
<span class="text-white font-black text-sm leading-none block">{{ branding.name }}</span>
|
|
13
|
+
<span class="text-white/30 text-[9px] font-medium">Admin CMS</span>
|
|
14
|
+
</div>
|
|
15
|
+
</NuxtLink>
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
<!-- Nav -->
|
|
19
|
+
<nav class="flex-1 px-3 pb-4 space-y-0.5 overflow-y-auto">
|
|
20
|
+
|
|
21
|
+
<!-- Dashboard -->
|
|
22
|
+
<NuxtLink to="/admin"
|
|
23
|
+
:class="[
|
|
24
|
+
'flex items-center gap-2.5 px-3 py-2 rounded-lg text-[11px] font-semibold transition-all',
|
|
25
|
+
route.path === '/admin' ? 'bg-white text-black' : 'text-white/50 hover:text-white hover:bg-white/[0.08]'
|
|
26
|
+
]">
|
|
27
|
+
<svg class="w-3.5 h-3.5 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
28
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"/>
|
|
29
|
+
</svg>
|
|
30
|
+
Tableau de bord
|
|
31
|
+
</NuxtLink>
|
|
32
|
+
|
|
33
|
+
<!-- Pages section -->
|
|
34
|
+
<div v-if="pageTypes.length" class="pt-4 pb-1.5 px-3">
|
|
35
|
+
<p class="text-[8px] font-black uppercase text-white/20 tracking-[0.15em]">Pages</p>
|
|
36
|
+
</div>
|
|
37
|
+
|
|
38
|
+
<NuxtLink v-for="pt in pageTypes" :key="pt.id" :to="`/admin/pages/${pt.id}`"
|
|
39
|
+
:class="[
|
|
40
|
+
'flex items-center gap-2.5 px-3 py-2 rounded-lg text-[11px] font-semibold transition-all',
|
|
41
|
+
route.path === `/admin/pages/${pt.id}` ? 'bg-white text-black' : 'text-white/50 hover:text-white hover:bg-white/[0.08]'
|
|
42
|
+
]">
|
|
43
|
+
<svg class="w-3.5 h-3.5 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"
|
|
44
|
+
v-html="pt.svgPath || defaultIcons[pt.id] || defaultIcons.blog_article">
|
|
45
|
+
</svg>
|
|
46
|
+
{{ pt.label }}
|
|
47
|
+
</NuxtLink>
|
|
48
|
+
|
|
49
|
+
<!-- Extra nav sections (e.g. Templates, Messages) -->
|
|
50
|
+
<template v-if="navSections.length">
|
|
51
|
+
<div class="pt-4 pb-1.5 px-3">
|
|
52
|
+
<p class="text-[8px] font-black uppercase text-white/20 tracking-[0.15em]">Contenu</p>
|
|
53
|
+
</div>
|
|
54
|
+
<NuxtLink v-for="item in navSections" :key="item.to" :to="item.to"
|
|
55
|
+
:class="[
|
|
56
|
+
'flex items-center gap-2.5 px-3 py-2 rounded-lg text-[11px] font-semibold transition-all',
|
|
57
|
+
route.path.startsWith(item.to) ? 'bg-white text-black' : 'text-white/50 hover:text-white hover:bg-white/[0.08]'
|
|
58
|
+
]">
|
|
59
|
+
<svg class="w-3.5 h-3.5 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"
|
|
60
|
+
v-html="item.svgPath || defaultNavIcon">
|
|
61
|
+
</svg>
|
|
62
|
+
{{ item.label }}
|
|
63
|
+
</NuxtLink>
|
|
64
|
+
</template>
|
|
65
|
+
|
|
66
|
+
</nav>
|
|
67
|
+
|
|
68
|
+
<!-- Bottom -->
|
|
69
|
+
<div class="px-3 py-4 border-t border-white/[0.06] space-y-0.5">
|
|
70
|
+
<NuxtLink to="/admin/account"
|
|
71
|
+
:class="[
|
|
72
|
+
'flex items-center gap-2.5 px-3 py-2 rounded-lg text-[11px] font-semibold transition-all',
|
|
73
|
+
route.path === '/admin/account' ? 'bg-white text-black' : 'text-white/50 hover:text-white hover:bg-white/[0.08]'
|
|
74
|
+
]">
|
|
75
|
+
<svg class="w-3.5 h-3.5 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
76
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"/>
|
|
77
|
+
</svg>
|
|
78
|
+
Mon compte
|
|
79
|
+
</NuxtLink>
|
|
80
|
+
<button @click="handleLogout" class="w-full flex items-center gap-2.5 px-3 py-2 rounded-lg text-[11px] font-semibold text-white/30 hover:text-red-400 hover:bg-red-500/5 transition-all">
|
|
81
|
+
<svg class="w-3.5 h-3.5 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
82
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"/>
|
|
83
|
+
</svg>
|
|
84
|
+
Déconnexion
|
|
85
|
+
</button>
|
|
86
|
+
</div>
|
|
87
|
+
</aside>
|
|
88
|
+
</template>
|
|
89
|
+
|
|
90
|
+
<script setup>
|
|
91
|
+
import { computed, useRoute, useRuntimeConfig, useSupabaseClient, navigateTo } from "#imports";
|
|
92
|
+
const route = useRoute();
|
|
93
|
+
const config = useRuntimeConfig().public.adminCms;
|
|
94
|
+
const branding = computed(() => ({
|
|
95
|
+
name: config?.branding?.name || "Admin",
|
|
96
|
+
logoUrl: config?.branding?.logoUrl || ""
|
|
97
|
+
}));
|
|
98
|
+
const brandInitials = computed(() => branding.value.name.substring(0, 2).toUpperCase());
|
|
99
|
+
const pageTypes = computed(() => config?.pageTypes || []);
|
|
100
|
+
const navSections = computed(() => config?.navSections || []);
|
|
101
|
+
const defaultIcons = {
|
|
102
|
+
landing_page: '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"/>',
|
|
103
|
+
service_page: '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 13.255A23.931 23.931 0 0112 15c-3.183 0-6.22-.62-9-1.745M16 6V4a2 2 0 00-2-2h-4a2 2 0 00-2 2v2m4 6h.01M5 20h14a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"/>',
|
|
104
|
+
about_page: '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0z"/>',
|
|
105
|
+
product_page: '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4"/>',
|
|
106
|
+
blog_article: '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/>',
|
|
107
|
+
portfolio_item: '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"/>'
|
|
108
|
+
};
|
|
109
|
+
const defaultNavIcon = '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1"/>';
|
|
110
|
+
const handleLogout = async () => {
|
|
111
|
+
const supabase = useSupabaseClient();
|
|
112
|
+
await supabase.auth.signOut();
|
|
113
|
+
navigateTo(config?.loginRoute || "/");
|
|
114
|
+
};
|
|
115
|
+
</script>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { useSupabaseClient } from "#imports";
|
|
2
|
+
export const useAdminBranding = () => {
|
|
3
|
+
const supabase = useSupabaseClient();
|
|
4
|
+
const fetchBranding = async () => {
|
|
5
|
+
const { data, error } = await supabase.from("branding").select("*").limit(1).single();
|
|
6
|
+
if (error && error.code !== "PGRST116") throw error;
|
|
7
|
+
return data || null;
|
|
8
|
+
};
|
|
9
|
+
const saveBranding = async (brandingData) => {
|
|
10
|
+
const existing = await fetchBranding();
|
|
11
|
+
if (existing?.id) {
|
|
12
|
+
const { data, error } = await supabase.from("branding").update(brandingData).eq("id", existing.id).select().single();
|
|
13
|
+
if (error) throw error;
|
|
14
|
+
return data;
|
|
15
|
+
} else {
|
|
16
|
+
const { data, error } = await supabase.from("branding").insert(brandingData).select().single();
|
|
17
|
+
if (error) throw error;
|
|
18
|
+
return data;
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
return { fetchBranding, saveBranding };
|
|
22
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare const useAdminContent: () => {
|
|
2
|
+
fetchContent: () => Promise<any>;
|
|
3
|
+
getContentByPersona: (personaId: string) => Promise<any>;
|
|
4
|
+
getContentByStatus: (status: string) => Promise<any>;
|
|
5
|
+
createContent: (content: any) => Promise<any>;
|
|
6
|
+
updateContent: (id: string, updates: any) => Promise<any>;
|
|
7
|
+
deleteContent: (id: string) => Promise<boolean>;
|
|
8
|
+
publishContent: (id: string) => Promise<any>;
|
|
9
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { useSupabaseClient } from "#imports";
|
|
2
|
+
export const useAdminContent = () => {
|
|
3
|
+
const supabase = useSupabaseClient();
|
|
4
|
+
const fetchContent = async () => {
|
|
5
|
+
const { data, error } = await supabase.from("generated_content").select("*").order("created_at", { ascending: false });
|
|
6
|
+
if (error) throw error;
|
|
7
|
+
return data || [];
|
|
8
|
+
};
|
|
9
|
+
const getContentByPersona = async (personaId) => {
|
|
10
|
+
const { data, error } = await supabase.from("generated_content").select("*").eq("persona_id", personaId).order("created_at", { ascending: false });
|
|
11
|
+
if (error) throw error;
|
|
12
|
+
return data || [];
|
|
13
|
+
};
|
|
14
|
+
const getContentByStatus = async (status) => {
|
|
15
|
+
const { data, error } = await supabase.from("generated_content").select("*").eq("status", status).order("created_at", { ascending: false });
|
|
16
|
+
if (error) throw error;
|
|
17
|
+
return data || [];
|
|
18
|
+
};
|
|
19
|
+
const createContent = async (content) => {
|
|
20
|
+
const { data, error } = await supabase.from("generated_content").insert(content).select().single();
|
|
21
|
+
if (error) throw error;
|
|
22
|
+
return data;
|
|
23
|
+
};
|
|
24
|
+
const updateContent = async (id, updates) => {
|
|
25
|
+
const { data, error } = await supabase.from("generated_content").update(updates).eq("id", id).select().single();
|
|
26
|
+
if (error) throw error;
|
|
27
|
+
return data;
|
|
28
|
+
};
|
|
29
|
+
const deleteContent = async (id) => {
|
|
30
|
+
const { error } = await supabase.from("generated_content").delete().eq("id", id);
|
|
31
|
+
if (error) throw error;
|
|
32
|
+
return true;
|
|
33
|
+
};
|
|
34
|
+
const publishContent = async (id) => {
|
|
35
|
+
return updateContent(id, { status: "published", published_at: (/* @__PURE__ */ new Date()).toISOString() });
|
|
36
|
+
};
|
|
37
|
+
return {
|
|
38
|
+
fetchContent,
|
|
39
|
+
getContentByPersona,
|
|
40
|
+
getContentByStatus,
|
|
41
|
+
createContent,
|
|
42
|
+
updateContent,
|
|
43
|
+
deleteContent,
|
|
44
|
+
publishContent
|
|
45
|
+
};
|
|
46
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare const useAdminPersonas: () => {
|
|
2
|
+
fetchPersonas: () => Promise<any>;
|
|
3
|
+
fetchPersonaById: (id: string) => Promise<any>;
|
|
4
|
+
createPersona: (persona: any) => Promise<any>;
|
|
5
|
+
updatePersona: (id: string, updates: any) => Promise<any>;
|
|
6
|
+
deletePersona: (id: string) => Promise<boolean>;
|
|
7
|
+
archivePersona: (id: string) => Promise<any>;
|
|
8
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { useSupabaseClient } from "#imports";
|
|
2
|
+
export const useAdminPersonas = () => {
|
|
3
|
+
const supabase = useSupabaseClient();
|
|
4
|
+
const fetchPersonas = async () => {
|
|
5
|
+
const { data, error } = await supabase.from("personas").select("*").eq("is_archived", false).order("created_at", { ascending: false });
|
|
6
|
+
if (error) throw error;
|
|
7
|
+
return data || [];
|
|
8
|
+
};
|
|
9
|
+
const fetchPersonaById = async (id) => {
|
|
10
|
+
const { data, error } = await supabase.from("personas").select("*").eq("id", id).single();
|
|
11
|
+
if (error) throw error;
|
|
12
|
+
return data;
|
|
13
|
+
};
|
|
14
|
+
const createPersona = async (persona) => {
|
|
15
|
+
const { data, error } = await supabase.from("personas").insert(persona).select().single();
|
|
16
|
+
if (error) throw error;
|
|
17
|
+
return data;
|
|
18
|
+
};
|
|
19
|
+
const updatePersona = async (id, updates) => {
|
|
20
|
+
const { data, error } = await supabase.from("personas").update(updates).eq("id", id).select().single();
|
|
21
|
+
if (error) throw error;
|
|
22
|
+
return data;
|
|
23
|
+
};
|
|
24
|
+
const deletePersona = async (id) => {
|
|
25
|
+
const { error } = await supabase.from("personas").delete().eq("id", id);
|
|
26
|
+
if (error) throw error;
|
|
27
|
+
return true;
|
|
28
|
+
};
|
|
29
|
+
const archivePersona = async (id) => updatePersona(id, { is_archived: true });
|
|
30
|
+
return { fetchPersonas, fetchPersonaById, createPersona, updatePersona, deletePersona, archivePersona };
|
|
31
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { useSupabaseClient } from "#imports";
|
|
2
|
+
export const useAdminProducts = () => {
|
|
3
|
+
const supabase = useSupabaseClient();
|
|
4
|
+
const fetchProducts = async () => {
|
|
5
|
+
const { data, error } = await supabase.from("products").select("*").order("priority", { ascending: false }).order("created_at", { ascending: false });
|
|
6
|
+
if (error) throw error;
|
|
7
|
+
return data || [];
|
|
8
|
+
};
|
|
9
|
+
const createProduct = async (product) => {
|
|
10
|
+
const { data, error } = await supabase.from("products").insert([product]).select().single();
|
|
11
|
+
if (error) throw error;
|
|
12
|
+
return data;
|
|
13
|
+
};
|
|
14
|
+
const updateProduct = async (id, product) => {
|
|
15
|
+
const { data, error } = await supabase.from("products").update(product).eq("id", id).select().single();
|
|
16
|
+
if (error) throw error;
|
|
17
|
+
return data;
|
|
18
|
+
};
|
|
19
|
+
const deleteProduct = async (id) => {
|
|
20
|
+
const { error } = await supabase.from("products").delete().eq("id", id);
|
|
21
|
+
if (error) throw error;
|
|
22
|
+
};
|
|
23
|
+
return { fetchProducts, createProduct, updateProduct, deleteProduct };
|
|
24
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const useAdminStrategies: () => {
|
|
2
|
+
fetchStrategies: () => Promise<any>;
|
|
3
|
+
fetchStrategyById: (id: string) => Promise<any>;
|
|
4
|
+
createStrategy: (strategy: any) => Promise<any>;
|
|
5
|
+
updateStrategy: (id: string, updates: any) => Promise<any>;
|
|
6
|
+
deleteStrategy: (id: string) => Promise<boolean>;
|
|
7
|
+
fetchStrategyContents: (strategyId: string) => Promise<any>;
|
|
8
|
+
fetchStrategyPersonas: (strategyId: string) => Promise<any>;
|
|
9
|
+
fetchCompleteStrategy: (strategyId: string) => Promise<any>;
|
|
10
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { useSupabaseClient } from "#imports";
|
|
2
|
+
export const useAdminStrategies = () => {
|
|
3
|
+
const supabase = useSupabaseClient();
|
|
4
|
+
const fetchStrategies = async () => {
|
|
5
|
+
const { data, error } = await supabase.from("strategies").select("*").order("created_at", { ascending: false });
|
|
6
|
+
if (error) throw error;
|
|
7
|
+
return data || [];
|
|
8
|
+
};
|
|
9
|
+
const fetchStrategyById = async (id) => {
|
|
10
|
+
const { data, error } = await supabase.from("strategies").select("*").eq("id", id).single();
|
|
11
|
+
if (error) throw error;
|
|
12
|
+
return data;
|
|
13
|
+
};
|
|
14
|
+
const createStrategy = async (strategy) => {
|
|
15
|
+
const { data, error } = await supabase.from("strategies").insert(strategy).select().single();
|
|
16
|
+
if (error) throw error;
|
|
17
|
+
return data;
|
|
18
|
+
};
|
|
19
|
+
const updateStrategy = async (id, updates) => {
|
|
20
|
+
const { data, error } = await supabase.from("strategies").update(updates).eq("id", id).select().single();
|
|
21
|
+
if (error) throw error;
|
|
22
|
+
return data;
|
|
23
|
+
};
|
|
24
|
+
const deleteStrategy = async (id) => {
|
|
25
|
+
const { error } = await supabase.from("strategies").delete().eq("id", id);
|
|
26
|
+
if (error) throw error;
|
|
27
|
+
return true;
|
|
28
|
+
};
|
|
29
|
+
const fetchStrategyContents = async (strategyId) => {
|
|
30
|
+
const { data, error } = await supabase.from("strategy_contents").select("*, content:generated_content(*)").eq("strategy_id", strategyId).order("position", { ascending: true });
|
|
31
|
+
if (error) throw error;
|
|
32
|
+
return data || [];
|
|
33
|
+
};
|
|
34
|
+
const fetchStrategyPersonas = async (strategyId) => {
|
|
35
|
+
const { data, error } = await supabase.from("strategy_personas").select("*, persona:personas(*)").eq("strategy_id", strategyId);
|
|
36
|
+
if (error) throw error;
|
|
37
|
+
return data || [];
|
|
38
|
+
};
|
|
39
|
+
const fetchCompleteStrategy = async (strategyId) => {
|
|
40
|
+
const [strategy, contents, personas] = await Promise.all([
|
|
41
|
+
fetchStrategyById(strategyId),
|
|
42
|
+
fetchStrategyContents(strategyId),
|
|
43
|
+
fetchStrategyPersonas(strategyId)
|
|
44
|
+
]);
|
|
45
|
+
return { ...strategy, contents, personas };
|
|
46
|
+
};
|
|
47
|
+
return {
|
|
48
|
+
fetchStrategies,
|
|
49
|
+
fetchStrategyById,
|
|
50
|
+
createStrategy,
|
|
51
|
+
updateStrategy,
|
|
52
|
+
deleteStrategy,
|
|
53
|
+
fetchStrategyContents,
|
|
54
|
+
fetchStrategyPersonas,
|
|
55
|
+
fetchCompleteStrategy
|
|
56
|
+
};
|
|
57
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const useAdminTodos: () => {
|
|
2
|
+
fetchTodos: () => Promise<any>;
|
|
3
|
+
getActiveTodos: () => Promise<any>;
|
|
4
|
+
getTodosByDateRange: (startDate: Date, endDate: Date) => Promise<any>;
|
|
5
|
+
createTodo: (todo: any) => Promise<any>;
|
|
6
|
+
updateTodo: (id: string, updates: any) => Promise<any>;
|
|
7
|
+
toggleTodo: (id: string) => Promise<any>;
|
|
8
|
+
deleteTodo: (id: string) => Promise<boolean>;
|
|
9
|
+
isOverdue: (todo: any) => boolean;
|
|
10
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { useSupabaseClient } from "#imports";
|
|
2
|
+
export const useAdminTodos = () => {
|
|
3
|
+
const supabase = useSupabaseClient();
|
|
4
|
+
const fetchTodos = async () => {
|
|
5
|
+
const { data, error } = await supabase.from("todos").select("*").order("created_at", { ascending: false });
|
|
6
|
+
if (error) throw error;
|
|
7
|
+
return data || [];
|
|
8
|
+
};
|
|
9
|
+
const getActiveTodos = async () => {
|
|
10
|
+
const { data, error } = await supabase.from("todos").select("*").eq("completed", false).order("due_date", { ascending: true, nullsFirst: false });
|
|
11
|
+
if (error) throw error;
|
|
12
|
+
return data || [];
|
|
13
|
+
};
|
|
14
|
+
const getTodosByDateRange = async (startDate, endDate) => {
|
|
15
|
+
const { data, error } = await supabase.from("todos").select("*").gte("due_date", startDate.toISOString()).lte("due_date", endDate.toISOString()).order("due_date", { ascending: true });
|
|
16
|
+
if (error) throw error;
|
|
17
|
+
return data || [];
|
|
18
|
+
};
|
|
19
|
+
const createTodo = async (todo) => {
|
|
20
|
+
const { data, error } = await supabase.from("todos").insert(todo).select().single();
|
|
21
|
+
if (error) throw error;
|
|
22
|
+
return data;
|
|
23
|
+
};
|
|
24
|
+
const updateTodo = async (id, updates) => {
|
|
25
|
+
const { data, error } = await supabase.from("todos").update(updates).eq("id", id).select().single();
|
|
26
|
+
if (error) throw error;
|
|
27
|
+
return data;
|
|
28
|
+
};
|
|
29
|
+
const toggleTodo = async (id) => {
|
|
30
|
+
const { data: current, error: fetchErr } = await supabase.from("todos").select("completed").eq("id", id).single();
|
|
31
|
+
if (fetchErr) throw fetchErr;
|
|
32
|
+
const { data, error } = await supabase.from("todos").update({ completed: !current.completed }).eq("id", id).select().single();
|
|
33
|
+
if (error) throw error;
|
|
34
|
+
return data;
|
|
35
|
+
};
|
|
36
|
+
const deleteTodo = async (id) => {
|
|
37
|
+
const { error } = await supabase.from("todos").delete().eq("id", id);
|
|
38
|
+
if (error) throw error;
|
|
39
|
+
return true;
|
|
40
|
+
};
|
|
41
|
+
const isOverdue = (todo) => {
|
|
42
|
+
if (!todo.due_date || todo.completed) return false;
|
|
43
|
+
return new Date(todo.due_date) < /* @__PURE__ */ new Date();
|
|
44
|
+
};
|
|
45
|
+
return {
|
|
46
|
+
fetchTodos,
|
|
47
|
+
getActiveTodos,
|
|
48
|
+
getTodosByDateRange,
|
|
49
|
+
createTodo,
|
|
50
|
+
updateTodo,
|
|
51
|
+
toggleTodo,
|
|
52
|
+
deleteTodo,
|
|
53
|
+
isOverdue
|
|
54
|
+
};
|
|
55
|
+
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
+
import { ref, computed, watch, onMounted, useRuntimeConfig, useHead, useSupabaseClient, useSupabaseUser } from "#imports";
|
|
2
3
|
import { useAdminUserStore } from "../../stores/adminUser";
|
|
3
4
|
const user = useSupabaseUser();
|
|
4
5
|
const client = useSupabaseClient();
|