mirror of
https://github.com/jjbrunton/Sauci.git
synced 2026-11-02 02:19:43 +00:00
feat(backend): add standalone data plane
Replace the Supabase application data plane with the Node/PostgreSQL API, migrate mobile, admin, web, and MCP callers, add controlled cutover tooling, and define hardened Dokploy infrastructure.
This commit is contained in:
1 parent
01bb8225ae
commit
006761d3b0
294 files changed
+16259
-6138
No files matched your search
@@ -1,7 +1,7 @@
|
||||
import { useEffect, useState, useCallback } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { supabase } from '@/config';
|
||||
import { auditedSupabase } from '@/hooks/useAuditedSupabase';
|
||||
import { adminData } from '@/lib/adminApi';
|
||||
import { auditedAdminData } from '@/hooks/useAuditedAdminData';
|
||||
import { useEntityForm } from '@/hooks/useEntityForm';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Button } from '@/components/ui/button';
|
||||
@@ -215,7 +215,7 @@ export function CategoriesPage() {
|
||||
const from = (page - 1) * pageSize;
|
||||
const to = from + pageSize - 1;
|
||||
|
||||
const { data: cats, error, count } = await supabase
|
||||
const { data: cats, error, count } = await adminData
|
||||
.from('categories')
|
||||
.select('*', { count: 'exact' })
|
||||
.order('sort_order', { ascending: true })
|
||||
@@ -231,11 +231,11 @@ export function CategoriesPage() {
|
||||
|
||||
if (categoryIds.length > 0) {
|
||||
const [{ data: packs }, { data: darePacks }] = await Promise.all([
|
||||
supabase
|
||||
adminData
|
||||
.from('question_packs')
|
||||
.select('category_id')
|
||||
.in('category_id', categoryIds),
|
||||
supabase
|
||||
adminData
|
||||
.from('dare_packs')
|
||||
.select('category_id')
|
||||
.in('category_id', categoryIds),
|
||||
@@ -310,11 +310,11 @@ export function CategoriesPage() {
|
||||
};
|
||||
|
||||
if (form.editingItem) {
|
||||
const { error } = await auditedSupabase.update('categories', form.editingItem.id, data);
|
||||
const { error } = await auditedAdminData.update('categories', form.editingItem.id, data);
|
||||
if (error) throw error;
|
||||
toast.success('Category updated');
|
||||
} else {
|
||||
const { error } = await auditedSupabase.insert('categories', {
|
||||
const { error } = await auditedAdminData.insert('categories', {
|
||||
...data,
|
||||
sort_order: totalCount,
|
||||
});
|
||||
@@ -338,7 +338,7 @@ export function CategoriesPage() {
|
||||
}
|
||||
|
||||
try {
|
||||
const { error } = await auditedSupabase.delete('categories', category.id);
|
||||
const { error } = await auditedAdminData.delete('categories', category.id);
|
||||
if (error) throw error;
|
||||
toast.success('Category deleted');
|
||||
fetchCategories();
|
||||
@@ -355,7 +355,7 @@ export function CategoriesPage() {
|
||||
c.id === category.id ? { ...c, is_public: !c.is_public } : c
|
||||
));
|
||||
|
||||
const { error } = await auditedSupabase.update('categories', category.id, {
|
||||
const { error } = await auditedAdminData.update('categories', category.id, {
|
||||
is_public: !category.is_public
|
||||
});
|
||||
|
||||
@@ -399,7 +399,7 @@ export function CategoriesPage() {
|
||||
// Persist new order to database
|
||||
try {
|
||||
const updates = newCategories.map((category, index) =>
|
||||
auditedSupabase.update('categories', category.id, { sort_order: index })
|
||||
auditedAdminData.update('categories', category.id, { sort_order: index })
|
||||
);
|
||||
|
||||
const results = await Promise.all(updates);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useEffect, useState, useCallback } from 'react';
|
||||
import { Link, useParams } from 'react-router-dom';
|
||||
import { supabase } from '@/config';
|
||||
import { auditedSupabase } from '@/hooks/useAuditedSupabase';
|
||||
import { adminData } from '@/lib/adminApi';
|
||||
import { auditedAdminData } from '@/hooks/useAuditedAdminData';
|
||||
import { useEntityForm } from '@/hooks/useEntityForm';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
@@ -116,7 +116,7 @@ export function DarePacksPage() {
|
||||
|
||||
// Fetch category info
|
||||
if (categoryId) {
|
||||
const { data: categoryData } = await supabase
|
||||
const { data: categoryData } = await adminData
|
||||
.from('categories')
|
||||
.select('id, name, icon')
|
||||
.eq('id', categoryId)
|
||||
@@ -127,7 +127,7 @@ export function DarePacksPage() {
|
||||
}
|
||||
|
||||
// Fetch dare packs
|
||||
let query = supabase
|
||||
let query = adminData
|
||||
.from('dare_packs')
|
||||
.select('*', { count: 'exact' })
|
||||
.order('sort_order', { ascending: true })
|
||||
@@ -148,7 +148,7 @@ export function DarePacksPage() {
|
||||
const dareCounts: Record<string, number> = {};
|
||||
|
||||
if (packIds.length > 0) {
|
||||
const { data: dares } = await supabase
|
||||
const { data: dares } = await adminData
|
||||
.from('dares')
|
||||
.select('pack_id')
|
||||
.in('pack_id', packIds);
|
||||
@@ -159,7 +159,7 @@ export function DarePacksPage() {
|
||||
}
|
||||
|
||||
// Fetch categories
|
||||
const { data: categoryData } = await supabase
|
||||
const { data: categoryData } = await adminData
|
||||
.from('categories')
|
||||
.select('id, name')
|
||||
.order('name');
|
||||
@@ -208,7 +208,7 @@ export function DarePacksPage() {
|
||||
form.setSaving(true);
|
||||
try {
|
||||
if (form.editingItem) {
|
||||
const { error } = await auditedSupabase.update('dare_packs', form.editingItem.id, {
|
||||
const { error } = await auditedAdminData.update('dare_packs', form.editingItem.id, {
|
||||
name: form.formData.name,
|
||||
description: form.formData.description || null,
|
||||
icon: form.formData.icon || null,
|
||||
@@ -220,7 +220,7 @@ export function DarePacksPage() {
|
||||
if (error) throw error;
|
||||
toast.success('Dare pack updated');
|
||||
} else {
|
||||
const { error } = await auditedSupabase.insert('dare_packs', {
|
||||
const { error } = await auditedAdminData.insert('dare_packs', {
|
||||
name: form.formData.name,
|
||||
description: form.formData.description || null,
|
||||
icon: form.formData.icon || null,
|
||||
@@ -250,7 +250,7 @@ export function DarePacksPage() {
|
||||
}
|
||||
|
||||
try {
|
||||
const { error } = await auditedSupabase.delete('dare_packs', pack.id);
|
||||
const { error } = await auditedAdminData.delete('dare_packs', pack.id);
|
||||
if (error) throw error;
|
||||
toast.success('Dare pack deleted');
|
||||
fetchData();
|
||||
@@ -273,8 +273,8 @@ export function DarePacksPage() {
|
||||
const targetOrder = targetPack.sort_order ?? targetIndex;
|
||||
|
||||
const [currentUpdate, targetUpdate] = await Promise.all([
|
||||
auditedSupabase.update('dare_packs', pack.id, { sort_order: targetOrder }),
|
||||
auditedSupabase.update('dare_packs', targetPack.id, { sort_order: currentOrder }),
|
||||
auditedAdminData.update('dare_packs', pack.id, { sort_order: targetOrder }),
|
||||
auditedAdminData.update('dare_packs', targetPack.id, { sort_order: currentOrder }),
|
||||
]);
|
||||
|
||||
if (currentUpdate.error || targetUpdate.error) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { supabase } from '@/config';
|
||||
import { auditedSupabase } from '@/hooks/useAuditedSupabase';
|
||||
import { adminData } from '@/lib/adminApi';
|
||||
import { auditedAdminData } from '@/hooks/useAuditedAdminData';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Textarea } from '@/components/ui/textarea';
|
||||
@@ -110,7 +110,7 @@ export function DaresPage() {
|
||||
setLoading(true);
|
||||
try {
|
||||
// Fetch pack info
|
||||
const { data: packData } = await supabase
|
||||
const { data: packData } = await adminData
|
||||
.from('dare_packs')
|
||||
.select('id, name, description, icon')
|
||||
.eq('id', packId)
|
||||
@@ -122,7 +122,7 @@ export function DaresPage() {
|
||||
const to = from + pageSize - 1;
|
||||
|
||||
// Fetch dares
|
||||
const { data: dareData, error, count } = await supabase
|
||||
const { data: dareData, error, count } = await adminData
|
||||
.from('dares')
|
||||
.select('*', { count: 'exact' })
|
||||
.eq('pack_id', packId)
|
||||
@@ -191,11 +191,11 @@ export function DaresPage() {
|
||||
};
|
||||
|
||||
if (editingDare) {
|
||||
const { error } = await auditedSupabase.update('dares', editingDare.id, dareData);
|
||||
const { error } = await auditedAdminData.update('dares', editingDare.id, dareData);
|
||||
if (error) throw error;
|
||||
toast.success('Dare updated');
|
||||
} else {
|
||||
const { error } = await auditedSupabase.insert('dares', {
|
||||
const { error } = await auditedAdminData.insert('dares', {
|
||||
...dareData,
|
||||
pack_id: packId,
|
||||
});
|
||||
@@ -219,7 +219,7 @@ export function DaresPage() {
|
||||
}
|
||||
|
||||
try {
|
||||
const { error } = await auditedSupabase.delete('dares', dare.id);
|
||||
const { error } = await auditedAdminData.delete('dares', dare.id);
|
||||
if (error) throw error;
|
||||
toast.success('Dare deleted');
|
||||
fetchData();
|
||||
@@ -259,7 +259,7 @@ export function DaresPage() {
|
||||
setBulkDeleting(true);
|
||||
try {
|
||||
await Promise.all(
|
||||
Array.from(selectedIds).map(id => auditedSupabase.delete('dares', id))
|
||||
Array.from(selectedIds).map(id => auditedAdminData.delete('dares', id))
|
||||
);
|
||||
toast.success(`${count} dare${count !== 1 ? 's' : ''} deleted`);
|
||||
setSelectedIds(new Set());
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { Link, useParams } from 'react-router-dom';
|
||||
import { supabase } from '@/config';
|
||||
import { auditedSupabase } from '@/hooks/useAuditedSupabase';
|
||||
import { adminData } from '@/lib/adminApi';
|
||||
import { auditedAdminData } from '@/hooks/useAuditedAdminData';
|
||||
import { useEntityForm } from '@/hooks/useEntityForm';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
@@ -103,7 +103,7 @@ export function PacksPage() {
|
||||
try {
|
||||
// Fetch category info
|
||||
if (categoryId) {
|
||||
const { data: cat } = await supabase
|
||||
const { data: cat } = await adminData
|
||||
.from('categories')
|
||||
.select('id, name, icon')
|
||||
.eq('id', categoryId)
|
||||
@@ -111,7 +111,7 @@ export function PacksPage() {
|
||||
setCategory(cat);
|
||||
}
|
||||
|
||||
const { data: categoriesData } = await supabase
|
||||
const { data: categoriesData } = await adminData
|
||||
.from('categories')
|
||||
.select('id, name')
|
||||
.order('name');
|
||||
@@ -121,7 +121,7 @@ export function PacksPage() {
|
||||
const to = from + pageSize - 1;
|
||||
|
||||
// Fetch packs
|
||||
let query = supabase
|
||||
let query = adminData
|
||||
.from('question_packs')
|
||||
.select('*', { count: 'exact' })
|
||||
.order('sort_order', { ascending: true })
|
||||
@@ -141,7 +141,7 @@ export function PacksPage() {
|
||||
const questionCounts: Record<string, number> = {};
|
||||
|
||||
if (packIds.length > 0) {
|
||||
const { data: questions } = await supabase
|
||||
const { data: questions } = await adminData
|
||||
.from('questions')
|
||||
.select('pack_id')
|
||||
.in('pack_id', packIds);
|
||||
@@ -152,7 +152,7 @@ export function PacksPage() {
|
||||
}
|
||||
|
||||
// Fetch all topics
|
||||
const { data: topicsData } = await supabase
|
||||
const { data: topicsData } = await adminData
|
||||
.from('topics')
|
||||
.select('id, name')
|
||||
.order('name');
|
||||
@@ -161,7 +161,7 @@ export function PacksPage() {
|
||||
// Fetch pack_topics relationships
|
||||
let packTopicsData: any[] = [];
|
||||
if (packIds.length > 0) {
|
||||
const { data } = await supabase
|
||||
const { data } = await adminData
|
||||
.from('pack_topics')
|
||||
.select('pack_id, topic_id, topics(id, name)')
|
||||
.in('pack_id', packIds);
|
||||
@@ -236,7 +236,7 @@ export function PacksPage() {
|
||||
? true
|
||||
: (form.formData.scheduled_release_at ? false : undefined);
|
||||
|
||||
const { error } = await auditedSupabase.update('question_packs', form.editingItem.id, {
|
||||
const { error } = await auditedAdminData.update('question_packs', form.editingItem.id, {
|
||||
name: form.formData.name,
|
||||
description: form.formData.description || null,
|
||||
icon: form.formData.icon || null,
|
||||
@@ -254,7 +254,7 @@ export function PacksPage() {
|
||||
if (error) throw error;
|
||||
packId = form.editingItem.id;
|
||||
} else {
|
||||
const { data, error } = await supabase
|
||||
const { data, error } = await adminData
|
||||
.from('question_packs')
|
||||
.insert({
|
||||
name: form.formData.name,
|
||||
@@ -279,10 +279,10 @@ export function PacksPage() {
|
||||
}
|
||||
|
||||
// Update topics
|
||||
await supabase.from('pack_topics').delete().eq('pack_id', packId);
|
||||
await adminData.from('pack_topics').delete().eq('pack_id', packId);
|
||||
|
||||
if (selectedTopicIds.size > 0) {
|
||||
const { error: topicsError } = await supabase
|
||||
const { error: topicsError } = await adminData
|
||||
.from('pack_topics')
|
||||
.insert(
|
||||
Array.from(selectedTopicIds).map(topicId => ({
|
||||
@@ -310,7 +310,7 @@ export function PacksPage() {
|
||||
}
|
||||
|
||||
try {
|
||||
const { error } = await auditedSupabase.delete('question_packs', pack.id);
|
||||
const { error } = await auditedAdminData.delete('question_packs', pack.id);
|
||||
if (error) throw error;
|
||||
toast.success('Pack deleted');
|
||||
fetchData();
|
||||
@@ -351,8 +351,8 @@ export function PacksPage() {
|
||||
const targetOrder = targetPack.sort_order ?? targetIndex;
|
||||
|
||||
const [currentUpdate, targetUpdate] = await Promise.all([
|
||||
auditedSupabase.update('question_packs', pack.id, { sort_order: targetOrder }),
|
||||
auditedSupabase.update('question_packs', targetPack.id, { sort_order: currentOrder }),
|
||||
auditedAdminData.update('question_packs', pack.id, { sort_order: targetOrder }),
|
||||
auditedAdminData.update('question_packs', targetPack.id, { sort_order: currentOrder }),
|
||||
]);
|
||||
|
||||
if (currentUpdate.error || targetUpdate.error) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { supabase } from '@/config';
|
||||
import { adminData } from '@/lib/adminApi';
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Skeleton } from '@/components/ui/skeleton';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
@@ -126,7 +126,7 @@ export function QuestionAnalyticsPage() {
|
||||
while (true) {
|
||||
const from = page * pageSize;
|
||||
const to = from + pageSize - 1;
|
||||
const { data, error } = await supabase
|
||||
const { data, error } = await adminData
|
||||
.from('questions')
|
||||
.select('id, pack_id, intensity, partner_text, inverse_of, question_type')
|
||||
.range(from, to);
|
||||
@@ -150,7 +150,7 @@ export function QuestionAnalyticsPage() {
|
||||
setLoading(true);
|
||||
try {
|
||||
// Build pack query with optional is_public filter
|
||||
let packQuery = supabase
|
||||
let packQuery = adminData
|
||||
.from('question_packs')
|
||||
.select('id, name, is_premium, is_explicit, is_public, category_id, category:categories(name)');
|
||||
|
||||
@@ -162,11 +162,11 @@ export function QuestionAnalyticsPage() {
|
||||
|
||||
const [packsResult, topicsResult, packTopicsResult, questions] = await Promise.all([
|
||||
packQuery,
|
||||
supabase
|
||||
adminData
|
||||
.from('topics')
|
||||
.select('id, name')
|
||||
.order('name'),
|
||||
supabase
|
||||
adminData
|
||||
.from('pack_topics')
|
||||
.select('pack_id, topic_id, topics(id, name)'),
|
||||
fetchAllQuestions(),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useState, useEffect, useCallback, useMemo } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { supabase } from '@/config';
|
||||
import { auditedSupabase } from '@/hooks/useAuditedSupabase';
|
||||
import { adminData } from '@/lib/adminApi';
|
||||
import { auditedAdminData } from '@/hooks/useAuditedAdminData';
|
||||
import { useRealtimeSubscription } from '@/hooks/useRealtimeSubscription';
|
||||
import { RealtimeStatusIndicator } from '@/components/RealtimeStatusIndicator';
|
||||
import { Button } from '@/components/ui/button';
|
||||
@@ -150,12 +150,12 @@ export function QuestionsPage() {
|
||||
const from = (page - 1) * pageSize;
|
||||
const to = from + pageSize - 1;
|
||||
|
||||
const packPromise = supabase
|
||||
const packPromise = adminData
|
||||
.from('question_packs')
|
||||
.select('id, name, description, icon')
|
||||
.eq('id', packId)
|
||||
.single();
|
||||
let questionsQuery = supabase
|
||||
let questionsQuery = adminData
|
||||
.from('questions')
|
||||
.select('*', { count: 'exact' })
|
||||
.eq('pack_id', packId)
|
||||
@@ -167,7 +167,7 @@ export function QuestionsPage() {
|
||||
}
|
||||
|
||||
const questionsPromise = questionsQuery;
|
||||
const allPackPromise = supabase
|
||||
const allPackPromise = adminData
|
||||
.from('questions')
|
||||
.select('id, text, inverse_of')
|
||||
.eq('pack_id', packId)
|
||||
@@ -250,7 +250,7 @@ export function QuestionsPage() {
|
||||
// Fetch all questions in pack for inverse_of dropdown
|
||||
const fetchAllPackQuestions = useCallback(async () => {
|
||||
if (!packId) return;
|
||||
const { data } = await supabase
|
||||
const { data } = await adminData
|
||||
.from('questions')
|
||||
.select('id, text, inverse_of')
|
||||
.eq('pack_id', packId)
|
||||
@@ -317,7 +317,7 @@ export function QuestionsPage() {
|
||||
setSaving(true);
|
||||
try {
|
||||
if (editingQuestion) {
|
||||
const { error } = await auditedSupabase.update('questions', editingQuestion.id, {
|
||||
const { error } = await auditedAdminData.update('questions', editingQuestion.id, {
|
||||
text: formData.text,
|
||||
partner_text: formData.partner_text || null,
|
||||
allowed_couple_genders: formData.allowed_couple_genders.length > 0 ? formData.allowed_couple_genders : null,
|
||||
@@ -331,7 +331,7 @@ export function QuestionsPage() {
|
||||
if (error) throw error;
|
||||
toast.success('Question updated');
|
||||
} else {
|
||||
const { error } = await auditedSupabase.insert('questions', {
|
||||
const { error } = await auditedAdminData.insert('questions', {
|
||||
pack_id: packId,
|
||||
text: formData.text,
|
||||
partner_text: formData.partner_text || null,
|
||||
@@ -363,7 +363,7 @@ export function QuestionsPage() {
|
||||
}
|
||||
|
||||
try {
|
||||
const { error } = await auditedSupabase.delete('questions', question.id);
|
||||
const { error } = await auditedAdminData.delete('questions', question.id);
|
||||
|
||||
if (error) throw error;
|
||||
toast.success('Question deleted');
|
||||
@@ -396,7 +396,7 @@ export function QuestionsPage() {
|
||||
|
||||
// Insert standalone questions first (no inverse_of)
|
||||
if (standaloneQuestions.length > 0) {
|
||||
const { error: standaloneError } = await auditedSupabase.insert(
|
||||
const { error: standaloneError } = await auditedAdminData.insert(
|
||||
'questions',
|
||||
standaloneQuestions.map(q => ({
|
||||
pack_id: packId,
|
||||
@@ -415,7 +415,7 @@ export function QuestionsPage() {
|
||||
if (pair.length >= 2) {
|
||||
// First question is primary (inverse_of = NULL)
|
||||
const primary = pair[0];
|
||||
const { data: primaryData, error: primaryError } = await auditedSupabase.insert(
|
||||
const { data: primaryData, error: primaryError } = await auditedAdminData.insert(
|
||||
'questions',
|
||||
[{
|
||||
pack_id: packId,
|
||||
@@ -432,7 +432,7 @@ export function QuestionsPage() {
|
||||
const primaryId = primaryData?.[0]?.id;
|
||||
if (primaryId) {
|
||||
const inverse = pair[1];
|
||||
const { error: inverseError } = await auditedSupabase.insert(
|
||||
const { error: inverseError } = await auditedAdminData.insert(
|
||||
'questions',
|
||||
[{
|
||||
pack_id: packId,
|
||||
@@ -449,7 +449,7 @@ export function QuestionsPage() {
|
||||
// Handle any additional questions in the pair (shouldn't happen, but just in case)
|
||||
for (let i = 2; i < pair.length; i++) {
|
||||
const extra = pair[i];
|
||||
const { error: extraError } = await auditedSupabase.insert(
|
||||
const { error: extraError } = await auditedAdminData.insert(
|
||||
'questions',
|
||||
[{
|
||||
pack_id: packId,
|
||||
@@ -464,7 +464,7 @@ export function QuestionsPage() {
|
||||
}
|
||||
} else if (pair.length === 1) {
|
||||
// Single question with pair_id but no pair - treat as standalone
|
||||
const { error } = await auditedSupabase.insert(
|
||||
const { error } = await auditedAdminData.insert(
|
||||
'questions',
|
||||
[{
|
||||
pack_id: packId,
|
||||
@@ -497,7 +497,7 @@ export function QuestionsPage() {
|
||||
|
||||
setReviewLoading(true);
|
||||
try {
|
||||
const { data, error } = await supabase
|
||||
const { data, error } = await adminData
|
||||
.from('questions')
|
||||
.select('id, pack_id, text, partner_text, intensity, allowed_couple_genders, target_user_genders, required_props, question_type, created_at')
|
||||
.eq('pack_id', packId)
|
||||
@@ -544,7 +544,7 @@ export function QuestionsPage() {
|
||||
setBulkDeleting(true);
|
||||
try {
|
||||
await Promise.all(
|
||||
Array.from(selectedIds).map(id => auditedSupabase.delete('questions', id))
|
||||
Array.from(selectedIds).map(id => auditedAdminData.delete('questions', id))
|
||||
);
|
||||
toast.success(`${count} question${count !== 1 ? 's' : ''} deleted`);
|
||||
setSelectedIds(new Set());
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { supabase } from '@/config';
|
||||
import { adminData } from '@/lib/adminApi';
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Skeleton } from '@/components/ui/skeleton';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
@@ -64,9 +64,9 @@ export function TagsPage() {
|
||||
setLoading(true);
|
||||
try {
|
||||
const [topicsResult, packTopicsResult, packsResult] = await Promise.all([
|
||||
supabase.from('topics').select('id, name').order('name'),
|
||||
supabase.from('pack_topics').select('pack_id, topic_id'),
|
||||
supabase
|
||||
adminData.from('topics').select('id, name').order('name'),
|
||||
adminData.from('pack_topics').select('pack_id, topic_id'),
|
||||
adminData
|
||||
.from('question_packs')
|
||||
.select('id, name, description, is_public, category:categories(name)')
|
||||
.order('name'),
|
||||
@@ -106,7 +106,7 @@ export function TagsPage() {
|
||||
const questionCounts: Record<string, number> = {};
|
||||
|
||||
if (untaggedPackIds.length > 0) {
|
||||
const { data: questions, error: questionsError } = await supabase
|
||||
const { data: questions, error: questionsError } = await adminData
|
||||
.from('questions')
|
||||
.select('pack_id')
|
||||
.in('pack_id', untaggedPackIds);
|
||||
@@ -167,7 +167,7 @@ export function TagsPage() {
|
||||
setTaggingProgress({ current: 0, total: eligiblePacks.length, packName: '' });
|
||||
|
||||
try {
|
||||
const { data: topicsData, error: topicsError } = await supabase
|
||||
const { data: topicsData, error: topicsError } = await adminData
|
||||
.from('topics')
|
||||
.select('id, name')
|
||||
.order('name');
|
||||
@@ -188,7 +188,7 @@ export function TagsPage() {
|
||||
});
|
||||
|
||||
try {
|
||||
const { data: questions, error: questionsError } = await supabase
|
||||
const { data: questions, error: questionsError } = await adminData
|
||||
.from('questions')
|
||||
.select('text')
|
||||
.eq('pack_id', pack.id);
|
||||
@@ -237,7 +237,7 @@ export function TagsPage() {
|
||||
});
|
||||
|
||||
if (newTopics.length > 0) {
|
||||
const { data: createdTopics, error: createError } = await supabase
|
||||
const { data: createdTopics, error: createError } = await adminData
|
||||
.from('topics')
|
||||
.insert(newTopics)
|
||||
.select('id, name');
|
||||
@@ -250,14 +250,14 @@ export function TagsPage() {
|
||||
});
|
||||
}
|
||||
|
||||
await supabase.from('pack_topics').delete().eq('pack_id', pack.id);
|
||||
await adminData.from('pack_topics').delete().eq('pack_id', pack.id);
|
||||
|
||||
if (topicIds.length === 0) {
|
||||
skippedCount += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
const { error: linkError } = await supabase
|
||||
const { error: linkError } = await adminData
|
||||
.from('pack_topics')
|
||||
.insert(topicIds.map((topicId) => ({
|
||||
pack_id: pack.id,
|
||||
|
||||
Reference in new issue
Block a user