Agent Skills: Admin Panel Builder

Expert assistant for creating and maintaining admin panel pages in the KR92 Bible Voice project. Use when creating admin pages, building admin components, integrating with admin navigation, or adding admin features. Also use for refactoring admin pages (splitting one page into multiple, extracting components, reorganizing tabs).

UncategorizedID: Spectaculous-Code/raamattu-nyt/admin-panel-builder

Install this agent skill to your local

pnpm dlx add-skill https://github.com/Spectaculous-Code/raamattu-nyt/tree/HEAD/.claude/skills/admin-panel-builder

Skill Files

Browse the full folder contents for admin-panel-builder.

Download Skill

Loading file tree…

.claude/skills/admin-panel-builder/SKILL.md

Skill Metadata

Name
admin-panel-builder
Description
Expert assistant for creating and maintaining admin panel pages in the KR92 Bible Voice project. Use when creating admin pages, building admin components, integrating with admin navigation, or adding admin features. Also use for refactoring admin pages (splitting one page into multiple, extracting components, reorganizing tabs).

Admin Panel Builder

Context Files (Read First)

For structure and conventions, read from Docs/context/:

  • Docs/context/repo-structure.md - File locations
  • Docs/context/conventions.md - Naming and patterns

Quick Reference

File Locations

src/pages/Admin*.tsx           # Admin page components
src/components/admin/*.tsx     # Manager components
src/components/admin/tokens/   # Token-specific components
App.tsx                        # Route definitions (lines 71-81)

Current Admin Pages

| Page | Path | Purpose | |------|------|---------| | Dashboard | /admin | Overview with section cards | | AI | /admin/ai | Prompts, pricing, quotas, features (6 tabs) | | App Config | /admin/app-config | Application configuration | | Audio | /admin/audio | TTS voices, version config (2 tabs) | | Auth Tokens | /admin/auth-tokens | API key management (collapsible sections) | | Backup | /admin/backup | Data backup management | | Bibles | /admin/bibles | Bible version management | | Changelog | /admin/changelog | Changelog entries | | Cinema | /admin/cinema | Background visuals, music (2 tabs) | | Encouragement | /admin/encouragement | Encouragement messages | | Errors | /admin/errors | JS + HTTP error reports | | FAQ | /admin/faq | FAQ management | | Feature Suggestions | /admin/feature-suggestions | User feature suggestions | | Feedback | /admin/feedback | App feedback | | I18n | /admin/i18n | Internationalization | | Maintenance Calendar | /admin/maintenance | Maintenance scheduling | | Onboarding | /admin/onboarding | Onboarding tours | | Practices | /admin/practices | Practice templates, prayer sets, spiritual paths (6 tabs) | | Prayers | /admin/prayers | Prayer calendars, prayer CRUD, JSON import | | Questions | /admin/questions | Q&A management | | Reading Plans | /admin/reading-plans | Bible reading plans (global + pending tabs) | | Recordings | /admin/recordings | Audio recordings | | Reels | /admin/reels | Video reels | | Slogans | /admin/slogans | Daily slogans | | Subscriptions | /admin/subscriptions | Plans, feature limits | | Systems | /admin/systems | System/app registry | | System Status | /admin/system-status | System health | | Task Templates | /admin/task-templates | Task template management | | Testing & Demos | /admin/testing | Component test pages | | Topics | /admin/topics | Topic suggestions, QA issues | | Translations | /admin/translations | Term translation cache | | Users | /admin/users | User management, history (3 tabs) | | Video | /admin/video | Video series/clips | | Widget Analytics | /admin/widget-analytics | Embed usage stats |

For detailed page structure (tabs, sections, components): See references/admin-panel-structure.md

Creating New Admin Page

1. Create Page File

// src/pages/AdminNewFeaturePage.tsx

import { useUserRole } from "@shared-auth/hooks/useUserRole";
import { SidebarProvider } from "@/components/ui/sidebar";
import { AppSidebar } from "@/components/AppSidebar";
import AdminHeader from "@/components/admin/AdminHeader";
import { FeatureIcon } from "lucide-react";

const AdminNewFeaturePage = () => {
  const { isAdmin } = useUserRole();

  if (!isAdmin) {
    return (
      <div className="flex items-center justify-center min-h-screen">
        <p className="text-muted-foreground">No permission</p>
      </div>
    );
  }

  return (
    <SidebarProvider>
      <div className="min-h-screen flex w-full bg-background">
        <AppSidebar onNavigateToContinueAudio={() => {}} onNavigateToContinueText={() => {}} />
        <main className="flex-1 overflow-auto">
          <AdminHeader
            title="New Feature"
            icon={<FeatureIcon className="h-6 w-6 text-primary" />}
          />
          <div className="p-6">
            <div className="max-w-6xl mx-auto space-y-8">
              {/* Content here */}
            </div>
          </div>
        </main>
      </div>
    </SidebarProvider>
  );
};

export default AdminNewFeaturePage;

2. Add Route

In App.tsx:

import AdminNewFeaturePage from "./pages/AdminNewFeaturePage";

// In routes section:
<Route path="/admin/new-feature" element={<AdminNewFeaturePage />} />

3. Add Dashboard Card

In AdminDashboardPage.tsx, add to sections array:

{
  title: "New Feature",
  description: "Description",
  icon: FeatureIcon,
  path: "/admin/new-feature",
}

Splitting Pages (Refactoring)

When a page has too many tabs or distinct functionality:

  1. Create new page file with subset of managers
  2. Add route in App.tsx
  3. Add dashboard card for new page
  4. Remove tabs from original page
  5. Update imports - remove unused managers

See references/refactoring.md for detailed steps.

AdminHeader Props

<AdminHeader
  title="Page Title"
  description="Optional subtitle"
  icon={<Icon className="h-6 w-6 text-primary" />}
  showBackButton={true}   // Shows back arrow to /admin
  showSidebarTrigger={true}
/>

Tab Structure Pattern

<Tabs defaultValue="first" className="space-y-4">
  <TabsList>
    <TabsTrigger value="first">First Tab</TabsTrigger>
    <TabsTrigger value="second">Second Tab</TabsTrigger>
  </TabsList>
  <TabsContent value="first">
    <Card>
      <CardHeader><CardTitle>Section</CardTitle></CardHeader>
      <CardContent><ManagerComponent /></CardContent>
    </Card>
  </TabsContent>
</Tabs>

Reusable Patterns

JSON Import Dialog

Pattern used in prayer calendar admin (AdminCalendarsTab.tsx):

function ImportDialog() {
  const [json, setJson] = useState("");
  const [result, setResult] = useState<{ success: number; failed: number } | null>(null);

  const handleImport = async () => {
    const parsed = JSON.parse(json);
    // Validate required fields
    // Batch insert with error tracking
    // Invalidate queries on success
    queryClient.invalidateQueries({ queryKey: ["items"] });
    setResult({ success: inserted, failed: errors });
  };

  return (
    <Dialog>
      <DialogTrigger asChild>
        <Button variant="outline"><Upload className="h-4 w-4 mr-2" />Tuo JSON</Button>
      </DialogTrigger>
      <DialogContent>
        <Textarea value={json} onChange={e => setJson(e.target.value)} rows={10} />
        <pre className="text-xs text-muted-foreground">{EXAMPLE_JSON}</pre>
        <Button onClick={handleImport}>Import</Button>
      </DialogContent>
    </Dialog>
  );
}

Summary-to-Entity Creation ("Luo koosteesta")

Pattern for creating entities from AI summaries:

<Button variant="outline" asChild>
  <Link to="/summaries">
    <List className="h-4 w-4 mr-2" />
    Luo koosteesta
  </Link>
</Button>

Used in: AdminReadingPlansPage for creating reading plans from summaries.

Essential Patterns

Role Check

const { isAdmin } = useUserRole();
if (!isAdmin) return <NoPermission />;

Data Query

const { data, isLoading } = useQuery({
  queryKey: ['feature'],
  queryFn: async () => {
    const { data, error } = await supabase.from('table').select('*');
    if (error) throw error;
    return data;
  },
});

Mutation with Toast

const mutation = useMutation({
  mutationFn: async (id: string) => {
    const { error } = await supabase.from('table').delete().eq('id', id);
    if (error) throw error;
  },
  onSuccess: () => {
    queryClient.invalidateQueries({ queryKey: ['feature'] });
    toast({ title: "Success" });
  },
  onError: (error: Error) => {
    toast({ title: "Error", description: error.message, variant: "destructive" });
  },
});

Cross-cutting learnings: See .claude/LEARNINGS.md → "Supabase/Database" section for RLS+GRANT patterns and admin role checks.

References