Agent skill
implement-admin-app
Plan and implement NovaTune Admin SPA with user management, track moderation, analytics, and audit logs (plan) (project)
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/other/other/implement-admin-app-tassadar2499-novatune-2
SKILL.md
Implement Admin App Skill
Build the NovaTune Admin application - the administration SPA with user management, track moderation, analytics dashboards, and audit log viewing.
Overview
The admin app (apps/admin) provides:
- Admin authentication with role verification
- User management (list, search, status updates)
- Track moderation (list, moderate, delete)
- Analytics dashboards (overview, per-track)
- Audit log viewing and integrity verification
Feature Areas
1. Admin Authentication
Files:
src/stores/auth.ts- Pinia auth store with admin role checksrc/features/auth/AdminLoginPage.vuesrc/composables/useAdminAuth.ts
Implementation:
// stores/auth.ts
export const useAuthStore = defineStore('auth', () => {
const accessToken = ref<string | null>(null);
const user = ref<AdminUser | null>(null);
const isAuthenticated = computed(() => !!accessToken.value);
const isAdmin = computed(() => user.value?.role === 'Admin');
async function login(email: string, password: string) {
const response = await authApi.login({ email, password, deviceId });
// Verify admin role
if (response.user.role !== 'Admin') {
throw new Error('Admin access required');
}
accessToken.value = response.accessToken;
user.value = response.user;
}
// ...
});
2. User Management
Files:
src/features/users/UsersListPage.vuesrc/features/users/UserDetailPage.vuesrc/features/users/components/UserTable.vuesrc/features/users/components/UserStatusBadge.vuesrc/features/users/components/UpdateStatusModal.vuesrc/features/users/composables/useUsers.ts
API Endpoints:
GET /admin/users- List users with search, filters, paginationPATCH /admin/users/{userId}/status- Update user status
Implementation:
// composables/useUsers.ts
export function useUsers(filters: Ref<UserFilters>) {
return useInfiniteQuery({
queryKey: ['admin', 'users', filters],
queryFn: ({ pageParam }) => adminApi.listUsers({
...filters.value,
cursor: pageParam,
}),
getNextPageParam: (lastPage) => lastPage.nextCursor,
});
}
export function useUpdateUserStatus() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({ userId, status, reasonCode }: UpdateStatusParams) =>
adminApi.updateUserStatus(userId, { status, reasonCode }),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['admin', 'users'] });
},
});
}
3. Track Moderation
Files:
src/features/tracks/TracksListPage.vuesrc/features/tracks/TrackDetailPage.vuesrc/features/tracks/components/TrackTable.vuesrc/features/tracks/components/ModerationActions.vuesrc/features/tracks/components/DeleteTrackModal.vuesrc/features/tracks/composables/useTracks.ts
API Endpoints:
GET /admin/tracks- List all tracks with filtersGET /admin/tracks/{trackId}- Get track detailsPATCH /admin/tracks/{trackId}/status- Moderate trackDELETE /admin/tracks/{trackId}- Delete track
Implementation:
// composables/useTracks.ts
export function useAdminTracks(filters: Ref<AdminTrackFilters>) {
return useInfiniteQuery({
queryKey: ['admin', 'tracks', filters],
queryFn: ({ pageParam }) => adminApi.listTracks({
...filters.value,
cursor: pageParam,
}),
getNextPageParam: (lastPage) => lastPage.nextCursor,
});
}
export function useModerateTrack() {
return useMutation({
mutationFn: ({ trackId, status, reasonCode }: ModerateParams) =>
adminApi.moderateTrack(trackId, { status, reasonCode }),
});
}
export function useDeleteTrack() {
return useMutation({
mutationFn: ({ trackId, reasonCode }: DeleteParams) =>
adminApi.deleteTrack(trackId, { reasonCode }),
});
}
4. Analytics Dashboard
Files:
src/features/analytics/DashboardPage.vuesrc/features/analytics/AnalyticsPage.vuesrc/features/analytics/components/OverviewCards.vuesrc/features/analytics/components/PlaybackChart.vuesrc/features/analytics/components/TopTracksTable.vuesrc/features/analytics/components/UserGrowthChart.vuesrc/features/analytics/composables/useAnalytics.ts
API Endpoints:
GET /admin/analytics/overview- Platform overview metricsGET /admin/analytics/tracks/{trackId}- Per-track analytics
Implementation:
// composables/useAnalytics.ts
export function useOverviewAnalytics(period: Ref<string>) {
return useQuery({
queryKey: ['admin', 'analytics', 'overview', period],
queryFn: () => adminApi.getOverviewAnalytics({ period: period.value }),
staleTime: 5 * 60 * 1000,
});
}
export function useTrackAnalytics(trackId: Ref<string>) {
return useQuery({
queryKey: ['admin', 'analytics', 'tracks', trackId],
queryFn: () => adminApi.getTrackAnalytics(trackId.value),
enabled: computed(() => !!trackId.value),
});
}
5. Audit Logs
Files:
src/features/audit/AuditLogPage.vuesrc/features/audit/components/AuditLogTable.vuesrc/features/audit/components/AuditLogFilters.vuesrc/features/audit/components/AuditLogDetail.vuesrc/features/audit/components/IntegrityVerification.vuesrc/features/audit/composables/useAuditLogs.ts
API Endpoints:
GET /admin/audit-logs- List audit logs with filtersPOST /admin/audit-logs/verify- Verify hash chain integrity
Implementation:
// composables/useAuditLogs.ts
export function useAuditLogs(filters: Ref<AuditLogFilters>) {
return useInfiniteQuery({
queryKey: ['admin', 'audit-logs', filters],
queryFn: ({ pageParam }) => adminApi.listAuditLogs({
...filters.value,
cursor: pageParam,
}),
getNextPageParam: (lastPage) => lastPage.nextCursor,
});
}
export function useVerifyIntegrity() {
return useMutation({
mutationFn: (params: { startDate?: string; endDate?: string }) =>
adminApi.verifyAuditLogIntegrity(params),
});
}
Router Configuration
// src/router/index.ts
const routes = [
{
path: '/auth',
children: [
{ path: 'login', name: 'login', component: () => import('@/features/auth/AdminLoginPage.vue') },
],
},
{
path: '/',
component: () => import('@/layouts/AdminLayout.vue'),
meta: { requiresAuth: true, requiresAdmin: true },
children: [
{ path: '', name: 'dashboard', component: () => import('@/features/analytics/DashboardPage.vue') },
{ path: 'users', name: 'users', component: () => import('@/features/users/UsersListPage.vue') },
{ path: 'users/:id', name: 'user-detail', component: () => import('@/features/users/UserDetailPage.vue') },
{ path: 'tracks', name: 'tracks', component: () => import('@/features/tracks/TracksListPage.vue') },
{ path: 'tracks/:id', name: 'track-detail', component: () => import('@/features/tracks/TrackDetailPage.vue') },
{ path: 'analytics', name: 'analytics', component: () => import('@/features/analytics/AnalyticsPage.vue') },
{ path: 'audit', name: 'audit', component: () => import('@/features/audit/AuditLogPage.vue') },
],
},
];
router.beforeEach((to, from, next) => {
const auth = useAuthStore();
if (to.meta.requiresAuth && !auth.isAuthenticated) {
next({ name: 'login' });
} else if (to.meta.requiresAdmin && !auth.isAdmin) {
next({ name: 'login' });
} else {
next();
}
});
Layout Components
AdminLayout.vue
<template>
<div class="flex h-screen">
<AdminSidebar />
<div class="flex flex-1 flex-col">
<AdminHeader />
<main class="flex-1 overflow-y-auto bg-gray-100 p-6">
<RouterView />
</main>
</div>
</div>
</template>
AdminSidebar.vue
<template>
<nav class="w-64 bg-gray-900 text-white">
<div class="p-4">
<h1 class="text-xl font-bold">NovaTune Admin</h1>
</div>
<ul class="space-y-1 p-2">
<SidebarLink to="/" icon="dashboard">Dashboard</SidebarLink>
<SidebarLink to="/users" icon="users">Users</SidebarLink>
<SidebarLink to="/tracks" icon="music">Tracks</SidebarLink>
<SidebarLink to="/analytics" icon="chart">Analytics</SidebarLink>
<SidebarLink to="/audit" icon="shield">Audit Logs</SidebarLink>
</ul>
</nav>
</template>
Testing Strategy
Unit Tests
// features/users/__tests__/UserTable.test.ts
describe('UserTable', () => {
it('renders user list', () => {
render(UserTable, {
props: { users: mockUsers },
});
expect(screen.getByText('[email protected]')).toBeInTheDocument();
});
it('shows status badge', () => {
render(UserTable, {
props: { users: [{ ...mockUser, status: 'Suspended' }] },
});
expect(screen.getByText('Suspended')).toHaveClass('bg-red-100');
});
});
E2E Tests
// e2e/admin.spec.ts
test('admin can view and moderate tracks', async ({ page }) => {
await loginAsAdmin(page);
await page.goto('/tracks');
await page.click('[data-testid="moderate-button"]');
await page.selectOption('[data-testid="status-select"]', 'Hidden');
await page.fill('[data-testid="reason-code"]', 'TOS_VIOLATION');
await page.click('[data-testid="confirm-button"]');
await expect(page.getByText('Track moderated')).toBeVisible();
});
Priority Order
- P0 - Core: Admin Auth, User Management, Track Moderation
- P1 - Essential: Analytics Dashboard, Audit Logs
- P2 - Enhancement: Integrity Verification
Related Documentation
- Frontend Plan:
doc/implementation/frontend/main.md - Stage 8 (Admin):
doc/implementation/stage-8-admin.md - Stage 7 (Telemetry):
doc/implementation/stage-7-telemetry.md - Requirements:
doc/requirements/functional/13-req-admin-moderation.md
Related Skills
- setup-vue-workspace - Create workspace first
- generate-api-client - Generate API client
- implement-player-app - Companion app
Claude Agents
- frontend-planner - Plan implementation
- vue-app-implementer - Implement features
- frontend-tester - Write tests
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?