diff --git a/frontends/nextjs/package.json b/frontends/nextjs/package.json index 7f1c6a225..a55e4a0f8 100644 --- a/frontends/nextjs/package.json +++ b/frontends/nextjs/package.json @@ -35,9 +35,12 @@ }, "devDependencies": { "@eslint/js": "^9.39.2", + "@tanstack/react-query": "^5.90.16", + "@testing-library/react": "^16.3.1", "@types/node": "^25.0.3", "@types/react": "^19.2.7", "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react-swc": "^4.2.2", "eslint": "^9.39.2", "eslint-plugin-react": "^7.37.5", "eslint-plugin-react-hooks": "^5.2.0", diff --git a/frontends/nextjs/src/app/_components/auth-provider/auth-provider-component.tsx b/frontends/nextjs/src/app/_components/auth-provider/auth-provider-component.tsx new file mode 100644 index 000000000..71f781dc5 --- /dev/null +++ b/frontends/nextjs/src/app/_components/auth-provider/auth-provider-component.tsx @@ -0,0 +1,17 @@ +/** + * Auth provider component (stub) + */ + +import type { ReactNode } from 'react' + +export interface AuthProviderProps { + children: ReactNode +} + +export function AuthProviderComponent({ children }: AuthProviderProps) { + // TODO: Implement auth provider + return children +} + +// Alias for compatibility +export const AuthProvider = AuthProviderComponent diff --git a/frontends/nextjs/src/app/_components/auth-provider/use-auth.ts b/frontends/nextjs/src/app/_components/auth-provider/use-auth.ts new file mode 100644 index 000000000..03377f642 --- /dev/null +++ b/frontends/nextjs/src/app/_components/auth-provider/use-auth.ts @@ -0,0 +1,26 @@ +/** + * useAuth hook (stub) + */ + +export interface AuthUser { + id: string + username: string + email: string + role: string + level: number +} + +export interface AuthState { + user: AuthUser | null + isLoading: boolean + isAuthenticated: boolean +} + +export function useAuth(): AuthState { + // TODO: Implement useAuth hook + return { + user: null, + isLoading: false, + isAuthenticated: false, + } +} diff --git a/frontends/nextjs/src/app/levels/levels-data.ts b/frontends/nextjs/src/app/levels/levels-data.ts new file mode 100644 index 000000000..87832ea86 --- /dev/null +++ b/frontends/nextjs/src/app/levels/levels-data.ts @@ -0,0 +1,12 @@ +/** + * Levels data (stub) + */ + +export const levelsData = { + public: 1, + user: 2, + moderator: 3, + admin: 4, + god: 5, + supergod: 6, +} diff --git a/frontends/nextjs/src/hooks/use-mobile.ts b/frontends/nextjs/src/hooks/use-mobile.ts new file mode 100644 index 000000000..87651ce1f --- /dev/null +++ b/frontends/nextjs/src/hooks/use-mobile.ts @@ -0,0 +1,8 @@ +/** + * useMobile hook (stub) + */ + +export function useMobile(): boolean { + // TODO: Implement mobile detection + return false +} diff --git a/frontends/nextjs/src/hooks/useAutoRefresh.ts b/frontends/nextjs/src/hooks/useAutoRefresh.ts new file mode 100644 index 000000000..ec760f2df --- /dev/null +++ b/frontends/nextjs/src/hooks/useAutoRefresh.ts @@ -0,0 +1,12 @@ +/** + * useAutoRefresh hook (stub) + */ + +export interface AutoRefreshOptions { + interval?: number + enabled?: boolean +} + +export function useAutoRefresh(callback: () => void, options?: AutoRefreshOptions): void { + // TODO: Implement auto refresh +} diff --git a/frontends/nextjs/src/hooks/useCodeEditor.ts b/frontends/nextjs/src/hooks/useCodeEditor.ts new file mode 100644 index 000000000..bc36a53d0 --- /dev/null +++ b/frontends/nextjs/src/hooks/useCodeEditor.ts @@ -0,0 +1,20 @@ +/** + * useCodeEditor hook (stub) + */ + +export interface EditorFile { + path: string + content: string + language?: string +} + +export function useCodeEditor() { + // TODO: Implement useCodeEditor + return { + files: [] as EditorFile[], + currentFile: null as EditorFile | null, + openFile: (file: EditorFile) => {}, + saveFile: (file: EditorFile) => {}, + closeFile: (path: string) => {}, + } +} diff --git a/frontends/nextjs/src/hooks/useDBAL.ts b/frontends/nextjs/src/hooks/useDBAL.ts new file mode 100644 index 000000000..2ba8a797a --- /dev/null +++ b/frontends/nextjs/src/hooks/useDBAL.ts @@ -0,0 +1,14 @@ +/** + * useDBAL hook (stub) + */ + +export function useDBAL() { + // TODO: Implement useDBAL + return { + get: async (entity: string, id: string) => null, + list: async (entity: string) => [], + create: async (entity: string, data: unknown) => {}, + update: async (entity: string, id: string, data: unknown) => {}, + delete: async (entity: string, id: string) => {}, + } +} diff --git a/frontends/nextjs/src/hooks/useFileTree.ts b/frontends/nextjs/src/hooks/useFileTree.ts new file mode 100644 index 000000000..9b9e6aba6 --- /dev/null +++ b/frontends/nextjs/src/hooks/useFileTree.ts @@ -0,0 +1,20 @@ +/** + * useFileTree hook (stub) + */ + +export interface FileNode { + name: string + path: string + type: 'file' | 'directory' + children?: FileNode[] +} + +export function useFileTree(rootPath?: string) { + // TODO: Implement useFileTree + return { + tree: null as FileNode | null, + loading: false, + error: null as Error | null, + refresh: () => {}, + } +} diff --git a/frontends/nextjs/src/hooks/useGitHubFetcher.ts b/frontends/nextjs/src/hooks/useGitHubFetcher.ts new file mode 100644 index 000000000..013c417ba --- /dev/null +++ b/frontends/nextjs/src/hooks/useGitHubFetcher.ts @@ -0,0 +1,21 @@ +/** + * useGitHubFetcher hook (stub) + */ + +export interface WorkflowRun { + id: number + name: string + status: string + conclusion?: string + createdAt: string +} + +export function useGitHubFetcher() { + // TODO: Implement useGitHubFetcher + return { + runs: [] as WorkflowRun[], + loading: false, + error: null as Error | null, + refetch: () => {}, + } +} diff --git a/frontends/nextjs/src/hooks/useKV.ts b/frontends/nextjs/src/hooks/useKV.ts new file mode 100644 index 000000000..a3098c424 --- /dev/null +++ b/frontends/nextjs/src/hooks/useKV.ts @@ -0,0 +1,13 @@ +/** + * useKV hook (stub) + */ + +export function useKV(namespace?: string) { + // TODO: Implement useKV + return { + get: async (key: string) => null, + set: async (key: string, value: unknown) => {}, + delete: async (key: string) => {}, + list: async (prefix?: string) => [], + } +} diff --git a/frontends/nextjs/src/lib/api/read-json.ts b/frontends/nextjs/src/lib/api/read-json.ts new file mode 100644 index 000000000..5ecd9bca2 --- /dev/null +++ b/frontends/nextjs/src/lib/api/read-json.ts @@ -0,0 +1,8 @@ +/** + * Read JSON from request (stub) + */ + +export async function readJson(request: Request): Promise { + // TODO: Implement JSON reading with validation + return await request.json() as T +} diff --git a/frontends/nextjs/src/lib/auth/api/fetch-session.ts b/frontends/nextjs/src/lib/auth/api/fetch-session.ts new file mode 100644 index 000000000..acd178270 --- /dev/null +++ b/frontends/nextjs/src/lib/auth/api/fetch-session.ts @@ -0,0 +1,15 @@ +/** + * Fetch current session (stub) + */ + +export interface Session { + id: string + userId: string + token: string + expiresAt: number +} + +export async function fetchSession(): Promise { + // TODO: Implement session fetching + return null +} diff --git a/frontends/nextjs/src/lib/auth/api/login.ts b/frontends/nextjs/src/lib/auth/api/login.ts new file mode 100644 index 000000000..fc66853ec --- /dev/null +++ b/frontends/nextjs/src/lib/auth/api/login.ts @@ -0,0 +1,19 @@ +/** + * Login API (stub) + */ + +export interface LoginCredentials { + username: string + password: string +} + +export interface LoginResponse { + success: boolean + token?: string + error?: string +} + +export async function login(credentials: LoginCredentials): Promise { + // TODO: Implement login + return { success: false, error: 'Not implemented' } +} diff --git a/frontends/nextjs/src/lib/auth/api/logout.ts b/frontends/nextjs/src/lib/auth/api/logout.ts new file mode 100644 index 000000000..3ef679971 --- /dev/null +++ b/frontends/nextjs/src/lib/auth/api/logout.ts @@ -0,0 +1,7 @@ +/** + * Logout API (stub) + */ + +export async function logout(): Promise { + // TODO: Implement logout +} diff --git a/frontends/nextjs/src/lib/auth/api/register.ts b/frontends/nextjs/src/lib/auth/api/register.ts new file mode 100644 index 000000000..ba384b7fa --- /dev/null +++ b/frontends/nextjs/src/lib/auth/api/register.ts @@ -0,0 +1,20 @@ +/** + * Register API (stub) + */ + +export interface RegisterData { + username: string + email: string + password: string +} + +export interface RegisterResponse { + success: boolean + userId?: string + error?: string +} + +export async function register(data: RegisterData): Promise { + // TODO: Implement registration + return { success: false, error: 'Not implemented' } +} diff --git a/frontends/nextjs/src/lib/compiler/index.ts b/frontends/nextjs/src/lib/compiler/index.ts new file mode 100644 index 000000000..aa5548d0f --- /dev/null +++ b/frontends/nextjs/src/lib/compiler/index.ts @@ -0,0 +1,18 @@ +/** + * Compiler utilities (stub) + */ + +export interface CompileOptions { + minify?: boolean + sourceMaps?: boolean +} + +export interface CompileResult { + code: string + map?: string +} + +export async function compile(source: string, options?: CompileOptions): Promise { + // TODO: Implement compilation + return { code: source } +} diff --git a/frontends/nextjs/src/lib/config/prisma.ts b/frontends/nextjs/src/lib/config/prisma.ts new file mode 100644 index 000000000..e0d194b1c --- /dev/null +++ b/frontends/nextjs/src/lib/config/prisma.ts @@ -0,0 +1,20 @@ +/** + * Prisma Client singleton instance + * Prevents multiple instances in development with hot reloading + */ + +import { PrismaClient } from '@prisma/client' + +const globalForPrisma = globalThis as unknown as { + prisma: PrismaClient | undefined +} + +export const prisma = + globalForPrisma.prisma ?? + new PrismaClient({ + log: process.env.NODE_ENV === 'development' ? ['error', 'warn'] : ['error'], + }) + +if (process.env.NODE_ENV !== 'production') { + globalForPrisma.prisma = prisma +} diff --git a/frontends/nextjs/src/lib/database-dbal/core/get-dbal.server.ts b/frontends/nextjs/src/lib/database-dbal/core/get-dbal.server.ts new file mode 100644 index 000000000..47f2d743e --- /dev/null +++ b/frontends/nextjs/src/lib/database-dbal/core/get-dbal.server.ts @@ -0,0 +1,7 @@ +/** + * Get DBAL client instance (stub) + */ + +import { getAdapter } from '../../dbal-client/adapter/get-adapter' + +export const getDBAL = getAdapter diff --git a/frontends/nextjs/src/lib/database-dbal/core/initialize-dbal.server.ts b/frontends/nextjs/src/lib/database-dbal/core/initialize-dbal.server.ts new file mode 100644 index 000000000..1abd04c5d --- /dev/null +++ b/frontends/nextjs/src/lib/database-dbal/core/initialize-dbal.server.ts @@ -0,0 +1,8 @@ +/** + * Initialize DBAL client (stub) + */ + +export async function initializeDBAL(): Promise { + // Stub: DBAL initialization handled by getAdapter + console.log('DBAL initialized (Prisma adapter)') +} diff --git a/frontends/nextjs/src/lib/database-dbal/users/dbal-add-user.server.ts b/frontends/nextjs/src/lib/database-dbal/users/dbal-add-user.server.ts new file mode 100644 index 000000000..291eee8fe --- /dev/null +++ b/frontends/nextjs/src/lib/database-dbal/users/dbal-add-user.server.ts @@ -0,0 +1,11 @@ +/** + * Add user via DBAL (stub) + */ + +import { getAdapter } from '../../dbal-client/adapter/get-adapter' +import type { User } from '../../types/level-types' + +export async function dbalAddUser(user: User): Promise { + const adapter = getAdapter() + await adapter.create('User', user) +} diff --git a/frontends/nextjs/src/lib/database-dbal/users/dbal-delete-user.server.ts b/frontends/nextjs/src/lib/database-dbal/users/dbal-delete-user.server.ts new file mode 100644 index 000000000..76595edec --- /dev/null +++ b/frontends/nextjs/src/lib/database-dbal/users/dbal-delete-user.server.ts @@ -0,0 +1,10 @@ +/** + * Delete user via DBAL (stub) + */ + +import { getAdapter } from '../../dbal-client/adapter/get-adapter' + +export async function dbalDeleteUser(id: string): Promise { + const adapter = getAdapter() + await adapter.delete('User', id) +} diff --git a/frontends/nextjs/src/lib/database-dbal/users/dbal-get-user-by-id.server.ts b/frontends/nextjs/src/lib/database-dbal/users/dbal-get-user-by-id.server.ts new file mode 100644 index 000000000..36ebb8fa0 --- /dev/null +++ b/frontends/nextjs/src/lib/database-dbal/users/dbal-get-user-by-id.server.ts @@ -0,0 +1,11 @@ +/** + * Get user by ID via DBAL (stub) + */ + +import { getAdapter } from '../../dbal-client/adapter/get-adapter' +import type { User } from '../../types/level-types' + +export async function dbalGetUserById(id: string): Promise { + const adapter = getAdapter() + return await adapter.get('User', id) as User | null +} diff --git a/frontends/nextjs/src/lib/database-dbal/users/dbal-get-users.server.ts b/frontends/nextjs/src/lib/database-dbal/users/dbal-get-users.server.ts new file mode 100644 index 000000000..10e6b4bf5 --- /dev/null +++ b/frontends/nextjs/src/lib/database-dbal/users/dbal-get-users.server.ts @@ -0,0 +1,12 @@ +/** + * Get users via DBAL (stub) + */ + +import { getAdapter } from '../../dbal-client/adapter/get-adapter' +import type { User } from '../../types/level-types' +import type { ListOptions, ListResult } from '../../dbal-client/types' + +export async function dbalGetUsers(options?: ListOptions): Promise> { + const adapter = getAdapter() + return await adapter.list('User', options) as ListResult +} diff --git a/frontends/nextjs/src/lib/database-dbal/users/dbal-update-user.server.ts b/frontends/nextjs/src/lib/database-dbal/users/dbal-update-user.server.ts new file mode 100644 index 000000000..042f8f394 --- /dev/null +++ b/frontends/nextjs/src/lib/database-dbal/users/dbal-update-user.server.ts @@ -0,0 +1,11 @@ +/** + * Update user via DBAL (stub) + */ + +import { getAdapter } from '../../dbal-client/adapter/get-adapter' +import type { User } from '../../types/level-types' + +export async function dbalUpdateUser(id: string, data: Partial): Promise { + const adapter = getAdapter() + return await adapter.update('User', id, data) as User +} diff --git a/frontends/nextjs/src/lib/db/password/index.ts b/frontends/nextjs/src/lib/db/password/index.ts new file mode 100644 index 000000000..1de941a2e --- /dev/null +++ b/frontends/nextjs/src/lib/db/password/index.ts @@ -0,0 +1,18 @@ +/** + * Password utilities and SMTP configuration types + */ + +export { hashPassword } from './hash-password' +export { verifyPassword } from './verify-password' + +// SMTP Configuration type +export interface SMTPConfig { + id?: string + host: string + port: number + secure: boolean + username: string + password: string + fromEmail: string + fromName: string +} diff --git a/frontends/nextjs/src/lib/dbal-client/adapter/close-adapter.ts b/frontends/nextjs/src/lib/dbal-client/adapter/close-adapter.ts new file mode 100644 index 000000000..f51850c0c --- /dev/null +++ b/frontends/nextjs/src/lib/dbal-client/adapter/close-adapter.ts @@ -0,0 +1,10 @@ +/** + * Close the DBAL adapter connection + */ + +import { getAdapter } from './get-adapter' + +export async function closeAdapter(): Promise { + const adapter = getAdapter() + await adapter.close() +} diff --git a/frontends/nextjs/src/lib/dbal-client/adapter/get-adapter.ts b/frontends/nextjs/src/lib/dbal-client/adapter/get-adapter.ts new file mode 100644 index 000000000..521ee208a --- /dev/null +++ b/frontends/nextjs/src/lib/dbal-client/adapter/get-adapter.ts @@ -0,0 +1,123 @@ +/** + * Get the current DBAL adapter instance + */ + +import { prisma } from '../../config/prisma' +import type { DBALAdapter, ListOptions, ListResult } from '../types' + +// Simple Prisma-based adapter implementation +class PrismaAdapter implements DBALAdapter { + async create(entity: string, data: Record): Promise { + const model = (prisma as any)[entity.toLowerCase()] + if (!model) throw new Error(`Unknown entity: ${entity}`) + return await model.create({ data }) + } + + async get(entity: string, id: string | number): Promise { + const model = (prisma as any)[entity.toLowerCase()] + if (!model) throw new Error(`Unknown entity: ${entity}`) + return await model.findUnique({ where: { id } }) + } + + async list(entity: string, options?: ListOptions): Promise { + const model = (prisma as any)[entity.toLowerCase()] + if (!model) throw new Error(`Unknown entity: ${entity}`) + + const where = options?.filter || {} + const orderBy = options?.orderBy ? { [options.orderBy]: options.orderDirection || 'asc' } : undefined + + const [data, total] = await Promise.all([ + model.findMany({ + where, + orderBy, + take: options?.limit, + skip: options?.offset, + }), + model.count({ where }), + ]) + + return { + data, + total, + hasMore: options?.limit ? (options.offset || 0) + data.length < total : false, + } + } + + async update(entity: string, id: string | number, data: Record): Promise { + const model = (prisma as any)[entity.toLowerCase()] + if (!model) throw new Error(`Unknown entity: ${entity}`) + return await model.update({ where: { id }, data }) + } + + async upsert( + entity: string, + uniqueField: string, + uniqueValue: unknown, + createData: Record, + updateData: Record + ): Promise { + const model = (prisma as any)[entity.toLowerCase()] + if (!model) throw new Error(`Unknown entity: ${entity}`) + return await model.upsert({ + where: { [uniqueField]: uniqueValue }, + create: createData, + update: updateData, + }) + } + + async delete(entity: string, id: string | number): Promise { + const model = (prisma as any)[entity.toLowerCase()] + if (!model) throw new Error(`Unknown entity: ${entity}`) + await model.delete({ where: { id } }) + } + + async createMany(entity: string, data: Record[]): Promise { + const model = (prisma as any)[entity.toLowerCase()] + if (!model) throw new Error(`Unknown entity: ${entity}`) + await model.createMany({ data }) + return data + } + + async updateMany(entity: string, ids: (string | number)[], data: Record): Promise { + const model = (prisma as any)[entity.toLowerCase()] + if (!model) throw new Error(`Unknown entity: ${entity}`) + + const results = await Promise.all( + ids.map(id => model.update({ where: { id }, data })) + ) + return results + } + + async deleteMany(entity: string, ids: (string | number)[]): Promise { + const model = (prisma as any)[entity.toLowerCase()] + if (!model) throw new Error(`Unknown entity: ${entity}`) + await model.deleteMany({ where: { id: { in: ids } } }) + } + + async query(entity: string, filter: Record, options?: ListOptions): Promise { + return this.list(entity, { ...options, filter }) + } + + async count(entity: string, filter?: Record): Promise { + const model = (prisma as any)[entity.toLowerCase()] + if (!model) throw new Error(`Unknown entity: ${entity}`) + return await model.count({ where: filter || {} }) + } + + async transaction(fn: (adapter: DBALAdapter) => Promise): Promise { + return await prisma.$transaction(async () => fn(this)) + } + + async close(): Promise { + await prisma.$disconnect() + } +} + +let adapter: DBALAdapter | null = null + +export function getAdapter(): DBALAdapter { + if (!adapter) { + adapter = new PrismaAdapter() + } + return adapter +} diff --git a/frontends/nextjs/src/lib/dbal-client/types.ts b/frontends/nextjs/src/lib/dbal-client/types.ts new file mode 100644 index 000000000..283f30949 --- /dev/null +++ b/frontends/nextjs/src/lib/dbal-client/types.ts @@ -0,0 +1,54 @@ +/** + * DBAL Adapter types + */ + +export interface ListOptions { + limit?: number + offset?: number + orderBy?: string + orderDirection?: 'asc' | 'desc' + filter?: Record +} + +export interface ListResult { + data: T[] + total: number + hasMore: boolean +} + +export interface DBALAdapter { + // Create operations + create(entity: string, data: Record): Promise + + // Read operations + get(entity: string, id: string | number): Promise + list(entity: string, options?: ListOptions): Promise + + // Update operations + update(entity: string, id: string | number, data: Record): Promise + upsert( + entity: string, + uniqueField: string, + uniqueValue: unknown, + createData: Record, + updateData: Record + ): Promise + + // Delete operations + delete(entity: string, id: string | number): Promise + + // Batch operations + createMany(entity: string, data: Record[]): Promise + updateMany(entity: string, ids: (string | number)[], data: Record): Promise + deleteMany(entity: string, ids: (string | number)[]): Promise + + // Query operations + query(entity: string, filter: Record, options?: ListOptions): Promise + count(entity: string, filter?: Record): Promise + + // Transaction support + transaction(fn: (adapter: DBALAdapter) => Promise): Promise + + // Lifecycle + close(): Promise +} diff --git a/frontends/nextjs/src/lib/github/create-github-client.ts b/frontends/nextjs/src/lib/github/create-github-client.ts new file mode 100644 index 000000000..f66d3b8bb --- /dev/null +++ b/frontends/nextjs/src/lib/github/create-github-client.ts @@ -0,0 +1,12 @@ +/** + * Create GitHub client (stub) + */ + +export interface GitHubClient { + // Add methods as needed +} + +export function createGitHubClient(token?: string): GitHubClient { + // TODO: Implement GitHub client creation + return {} +} diff --git a/frontends/nextjs/src/lib/github/fetch-workflow-run-logs.ts b/frontends/nextjs/src/lib/github/fetch-workflow-run-logs.ts new file mode 100644 index 000000000..1e131418b --- /dev/null +++ b/frontends/nextjs/src/lib/github/fetch-workflow-run-logs.ts @@ -0,0 +1,46 @@ +/** + * Fetch workflow run logs (stub) + */ + +export interface WorkflowJob { + id: number + name: string + status: string + conclusion?: string +} + +export interface WorkflowRunLogs { + logs: string + runId: number + jobs?: WorkflowJob[] + logsText?: string + truncated?: boolean +} + +export interface FetchWorkflowRunLogsOptions { + client?: unknown + owner: string + repo: string + runId: number + tailLines?: number + failedOnly?: boolean +} + +export async function fetchWorkflowRunLogs( + options: FetchWorkflowRunLogsOptions +): Promise +export async function fetchWorkflowRunLogs( + owner: string, + repo: string, + runId: number, + options?: { tailLines?: number; failedOnly?: boolean } +): Promise +export async function fetchWorkflowRunLogs( + ownerOrOptions: string | FetchWorkflowRunLogsOptions, + repo?: string, + runId?: number, + options?: { tailLines?: number; failedOnly?: boolean } +): Promise { + // TODO: Implement log fetching + return null +} diff --git a/frontends/nextjs/src/lib/github/parse-workflow-run-logs-options.ts b/frontends/nextjs/src/lib/github/parse-workflow-run-logs-options.ts new file mode 100644 index 000000000..ee3421d90 --- /dev/null +++ b/frontends/nextjs/src/lib/github/parse-workflow-run-logs-options.ts @@ -0,0 +1,23 @@ +/** + * Parse workflow run logs options (stub) + */ + +export interface WorkflowRunLogsOptions { + tailLines?: number + failedOnly?: boolean + runName?: string + includeLogs?: boolean + jobLimit?: number +} + +export function parseWorkflowRunLogsOptions(search: string | URLSearchParams): WorkflowRunLogsOptions { + // TODO: Implement option parsing + const params = typeof search === 'string' ? new URLSearchParams(search) : search + return { + tailLines: params.get('tailLines') ? parseInt(params.get('tailLines')!) : undefined, + failedOnly: params.get('failedOnly') === 'true', + runName: params.get('runName') || undefined, + includeLogs: params.get('includeLogs') === 'true', + jobLimit: params.get('jobLimit') ? parseInt(params.get('jobLimit')!) : undefined, + } +} diff --git a/frontends/nextjs/src/lib/github/resolve-github-repo.ts b/frontends/nextjs/src/lib/github/resolve-github-repo.ts new file mode 100644 index 000000000..5acfeb3bc --- /dev/null +++ b/frontends/nextjs/src/lib/github/resolve-github-repo.ts @@ -0,0 +1,21 @@ +/** + * Resolve GitHub repository (stub) + */ + +export interface GitHubRepo { + owner: string + repo: string +} + +export function resolveGitHubRepo(params: URLSearchParams | string): GitHubRepo { + // TODO: Implement repo resolution + if (typeof params === 'string') { + const [owner, repo] = params.split('/') + return { owner: owner || '', repo: repo || '' } + } + + return { + owner: params.get('owner') || '', + repo: params.get('repo') || '', + } +} diff --git a/frontends/nextjs/src/lib/github/workflows/listing/list-workflow-runs.ts b/frontends/nextjs/src/lib/github/workflows/listing/list-workflow-runs.ts new file mode 100644 index 000000000..1791ec06d --- /dev/null +++ b/frontends/nextjs/src/lib/github/workflows/listing/list-workflow-runs.ts @@ -0,0 +1,21 @@ +/** + * List workflow runs (stub) + */ + +export interface WorkflowRun { + id: number + name: string + status: string + conclusion?: string + createdAt: string +} + +export async function listWorkflowRuns( + owner: string, + repo: string, + search?: string, + workflowId?: string +): Promise { + // TODO: Implement workflow runs listing + return [] +} diff --git a/frontends/nextjs/src/lib/lua/ui/generate-component-tree.ts b/frontends/nextjs/src/lib/lua/ui/generate-component-tree.ts new file mode 100644 index 000000000..0cea3646e --- /dev/null +++ b/frontends/nextjs/src/lib/lua/ui/generate-component-tree.ts @@ -0,0 +1,14 @@ +/** + * Generate component tree from Lua (stub) + */ + +export interface ComponentTree { + type: string + props?: Record + children?: ComponentTree[] +} + +export function generateComponentTree(luaScript: string): ComponentTree { + // TODO: Implement Lua component tree generation + return { type: 'div' } +} diff --git a/frontends/nextjs/src/lib/lua/ui/types/lua-ui-package.ts b/frontends/nextjs/src/lib/lua/ui/types/lua-ui-package.ts new file mode 100644 index 000000000..f163ef08c --- /dev/null +++ b/frontends/nextjs/src/lib/lua/ui/types/lua-ui-package.ts @@ -0,0 +1,9 @@ +/** + * Lua UI types (stub) + */ + +export interface LuaUIPackage { + id: string + name: string + components: unknown[] +} diff --git a/frontends/nextjs/src/lib/package-lib/package-export.ts b/frontends/nextjs/src/lib/package-lib/package-export.ts new file mode 100644 index 000000000..264ad6b2c --- /dev/null +++ b/frontends/nextjs/src/lib/package-lib/package-export.ts @@ -0,0 +1,6 @@ +/** + * Package export utilities + * Re-exports from packages module + */ + +export * from '../packages' diff --git a/frontends/nextjs/src/lib/packages/json/load-json-package.ts b/frontends/nextjs/src/lib/packages/json/load-json-package.ts new file mode 100644 index 000000000..4a6386546 --- /dev/null +++ b/frontends/nextjs/src/lib/packages/json/load-json-package.ts @@ -0,0 +1,14 @@ +/** + * Load JSON package (stub) + */ + +export interface JSONPackage { + id: string + components: unknown[] + metadata: unknown +} + +export async function loadJSONPackage(packageId: string): Promise { + // TODO: Implement JSON package loading + return null +} diff --git a/frontends/nextjs/src/lib/packages/package-glue/index.ts b/frontends/nextjs/src/lib/packages/package-glue/index.ts index 88d062cb7..4e638c006 100644 --- a/frontends/nextjs/src/lib/packages/package-glue/index.ts +++ b/frontends/nextjs/src/lib/packages/package-glue/index.ts @@ -30,3 +30,16 @@ export { getPackageScripts, getPackagesByCategory, } from './functions' + +// Package glue singleton (stub) +export const packageGlue = { + getPackage, + getPackageComponents, + getPackageScripts, + getPackagesByCategory, + checkDependencies, +} + +export function getPackageGlue() { + return packageGlue +} diff --git a/frontends/nextjs/src/lib/routing/auth/validate-package-route.ts b/frontends/nextjs/src/lib/routing/auth/validate-package-route.ts new file mode 100644 index 000000000..6d58155c7 --- /dev/null +++ b/frontends/nextjs/src/lib/routing/auth/validate-package-route.ts @@ -0,0 +1,27 @@ +/** + * Validate package route (stub) + */ + +export interface RouteValidationResult { + valid: boolean + error?: string +} + +export async function validatePackageRoute( + tenant: string, + packageId: string, + userId?: string +): Promise { + // TODO: Implement route validation + return { valid: true } +} + +export async function canBePrimaryPackage(packageId: string): Promise { + // TODO: Implement primary package check + return true +} + +export async function loadPackageMetadata(packageId: string): Promise { + // TODO: Implement package metadata loading + return null +} diff --git a/frontends/nextjs/src/lib/routing/index.ts b/frontends/nextjs/src/lib/routing/index.ts new file mode 100644 index 000000000..5e453afd1 --- /dev/null +++ b/frontends/nextjs/src/lib/routing/index.ts @@ -0,0 +1,13 @@ +/** + * Routing utilities (stub) + */ + +export function parseRoute(path: string): Record { + // TODO: Implement route parsing + return {} +} + +export function buildRoute(template: string, params: Record): string { + // TODO: Implement route building + return template +} diff --git a/frontends/nextjs/src/lib/routing/route-parser.ts b/frontends/nextjs/src/lib/routing/route-parser.ts new file mode 100644 index 000000000..c40b425f2 --- /dev/null +++ b/frontends/nextjs/src/lib/routing/route-parser.ts @@ -0,0 +1,32 @@ +/** + * Route parser (stub) + */ + +export interface ParsedRoute { + tenant?: string + package?: string + path?: string + params: Record +} + +export const RESERVED_PATHS = ['api', 'admin', 'auth', '_next', 'static'] + +export function parseRoute(url: string): ParsedRoute { + // TODO: Implement route parsing + return { params: {} } +} + +export function getPrefixedEntity(entity: string, prefix?: string): string { + // TODO: Implement entity prefixing + return prefix ? `${prefix}_${entity}` : entity +} + +export function getTableName(entity: string, tenantId?: string): string { + // TODO: Implement table name resolution + return entity.toLowerCase() +} + +export function isReservedPath(path: string): boolean { + // TODO: Implement reserved path checking + return RESERVED_PATHS.includes(path.split('/')[1] || path) +} diff --git a/frontends/nextjs/src/lib/schema/index.ts b/frontends/nextjs/src/lib/schema/index.ts new file mode 100644 index 000000000..cd6b86d05 --- /dev/null +++ b/frontends/nextjs/src/lib/schema/index.ts @@ -0,0 +1,6 @@ +/** + * Schema utilities and operations + * Re-exports from db/schemas module + */ + +export * from '../db/schemas' diff --git a/frontends/nextjs/src/lib/schema/schema-registry.ts b/frontends/nextjs/src/lib/schema/schema-registry.ts new file mode 100644 index 000000000..2e8610a29 --- /dev/null +++ b/frontends/nextjs/src/lib/schema/schema-registry.ts @@ -0,0 +1,59 @@ +/** + * Schema registry (stub) + */ + +import type { ModelSchema } from '../types/schema-types' + +export class SchemaRegistry { + private schemas: Map = new Map() + packages: Record = {} + + register(schema: ModelSchema): void { + this.schemas.set(schema.name, schema) + } + + get(name: string): ModelSchema | undefined { + return this.schemas.get(name) + } + + getAll(): ModelSchema[] { + return Array.from(this.schemas.values()) + } +} + +export const schemaRegistry = new SchemaRegistry() + +export function loadSchemaRegistry(path?: string): SchemaRegistry { + // TODO: Implement schema registry loading + return schemaRegistry +} + +export function saveSchemaRegistry(registry: SchemaRegistry, path?: string): void { + // TODO: Implement schema registry saving +} + +export interface PendingMigration { + id: string + packageId: string + status: string + queuedAt: string + entities: Array<{ name: string }> +} + +export function getPendingMigrations(registry: SchemaRegistry): PendingMigration[] { + // TODO: Implement pending migrations retrieval + return [] +} + +export function generatePrismaFragment(schema: ModelSchema, path?: string): string { + // TODO: Implement Prisma fragment generation + return '' +} + +export function approveMigration(registry: SchemaRegistry, migrationId: string): void { + // TODO: Implement migration approval +} + +export function rejectMigration(registry: SchemaRegistry, migrationId: string): void { + // TODO: Implement migration rejection +} diff --git a/frontends/nextjs/src/lib/seed/index.ts b/frontends/nextjs/src/lib/seed/index.ts new file mode 100644 index 000000000..ecb42d947 --- /dev/null +++ b/frontends/nextjs/src/lib/seed/index.ts @@ -0,0 +1,10 @@ +/** + * Seed data and database initialization utilities + * Re-exports from database-admin module + */ + +// Export seed functionality from database-admin +// This is a placeholder - actual implementation in db/database-admin +export const seedDefaultData = async () => { + console.warn('seedDefaultData: Not yet implemented') +} diff --git a/frontends/nextjs/src/lib/types/level-types.ts b/frontends/nextjs/src/lib/types/level-types.ts new file mode 100644 index 000000000..5c70f80c8 --- /dev/null +++ b/frontends/nextjs/src/lib/types/level-types.ts @@ -0,0 +1,75 @@ +/** + * Type definitions for core database entities + * These types match the Prisma schema models + */ + +export type UserRole = 'public' | 'user' | 'moderator' | 'admin' | 'god' | 'supergod' + +export interface User { + id: string + username: string + email: string + role: string + profilePicture?: string | null + bio?: string | null + createdAt: number | bigint + tenantId?: string | null + isInstanceOwner?: boolean + passwordChangeTimestamp?: number | bigint | null + firstLogin?: boolean +} + +export interface Tenant { + id: string + name: string + slug: string + ownerId: string + createdAt: number | bigint + homepageConfig?: string | null + settings?: string | null +} + +export interface PageConfig { + id: string + tenantId?: string | null + packageId?: string | null + path: string + title: string + description?: string | null + icon?: string | null + component?: string | null + luaScript?: string | null + accessLevel?: number | null + createdAt?: number | bigint + updatedAt?: number | bigint +} + +export interface Comment { + id: string + userId: string + entityType: string + entityId: string + content: string + createdAt: number | bigint + updatedAt?: number | bigint | null + parentId?: string | null +} + +export interface Workflow { + id: string + name: string + tenantId?: string | null + definition: string + status: string + createdAt: number | bigint + updatedAt?: number | bigint | null +} + +export interface AppConfiguration { + id: string + key: string + value: string + description?: string | null + createdAt?: number | bigint + updatedAt?: number | bigint | null +} diff --git a/frontends/nextjs/src/lib/types/schema-types.ts b/frontends/nextjs/src/lib/types/schema-types.ts new file mode 100644 index 000000000..54a71e60e --- /dev/null +++ b/frontends/nextjs/src/lib/types/schema-types.ts @@ -0,0 +1,19 @@ +/** + * Type definitions for schema-related entities + */ + +export interface ModelSchema { + id: string + tenantId?: string | null + name: string + label?: string | null + labelPlural?: string | null + icon?: string | null + fields: string // JSON: field definitions + listDisplay?: string | null // JSON: columns to show in list + listFilter?: string | null // JSON: filterable fields + searchFields?: string | null // JSON: searchable fields + ordering?: string | null // JSON: default sort order + validations?: string | null // JSON: validation rules + hooks?: string | null // JSON: lifecycle hooks (Lua script refs) +} diff --git a/frontends/nextjs/src/lib/ui-pages/load-page-from-db.ts b/frontends/nextjs/src/lib/ui-pages/load-page-from-db.ts new file mode 100644 index 000000000..7da78e20b --- /dev/null +++ b/frontends/nextjs/src/lib/ui-pages/load-page-from-db.ts @@ -0,0 +1,10 @@ +/** + * Load UI page from database (stub) + */ + +import type { PageConfig } from '../types/level-types' + +export async function loadPageFromDb(path: string, tenantId?: string): Promise { + // TODO: Implement page loading from database + return null +} diff --git a/frontends/nextjs/src/lib/ui-pages/load-page-from-lua-packages.ts b/frontends/nextjs/src/lib/ui-pages/load-page-from-lua-packages.ts new file mode 100644 index 000000000..cdbcc7134 --- /dev/null +++ b/frontends/nextjs/src/lib/ui-pages/load-page-from-lua-packages.ts @@ -0,0 +1,10 @@ +/** + * Load UI page from Lua packages (stub) + */ + +import type { PageConfig } from '../types/level-types' + +export async function loadPageFromLuaPackages(path: string): Promise { + // TODO: Implement page loading from Lua packages + return null +} diff --git a/frontends/nextjs/tsconfig.json b/frontends/nextjs/tsconfig.json index e003531a8..a161ded9e 100644 --- a/frontends/nextjs/tsconfig.json +++ b/frontends/nextjs/tsconfig.json @@ -60,10 +60,10 @@ "vitest.config.ts", "playwright.config.ts", ".next/types/**/*.ts", - ".next/dev/types/**/*.ts", - "../dbal/development/src/**/*.ts" + ".next/dev/types/**/*.ts" ], "exclude": [ - "node_modules" + "node_modules", + "../../dbal/development" ] } diff --git a/packages/admin_dialog/seed/metadata.json b/packages/admin_dialog/seed/metadata.json new file mode 100644 index 000000000..d5c44029d --- /dev/null +++ b/packages/admin_dialog/seed/metadata.json @@ -0,0 +1,12 @@ +{ + "packageId": "'$pkg'", + "name": "'$(echo $pkg | sed 's/_/ /g' | awk '{for(i=1;i<=NF;i++)sub(/./,toupper(substr($i,1,1)),$i)}1')'", + "version": "0.1.0", + "description": "Package '$pkg'", + "author": "MetaBuilder Team", + "category": "ui", + "exports": { + "components": [] + }, + "dependencies": [] +} diff --git a/packages/dashboard/seed/metadata.json b/packages/dashboard/seed/metadata.json new file mode 100644 index 000000000..d5c44029d --- /dev/null +++ b/packages/dashboard/seed/metadata.json @@ -0,0 +1,12 @@ +{ + "packageId": "'$pkg'", + "name": "'$(echo $pkg | sed 's/_/ /g' | awk '{for(i=1;i<=NF;i++)sub(/./,toupper(substr($i,1,1)),$i)}1')'", + "version": "0.1.0", + "description": "Package '$pkg'", + "author": "MetaBuilder Team", + "category": "ui", + "exports": { + "components": [] + }, + "dependencies": [] +} diff --git a/packages/data_table/seed/metadata.json b/packages/data_table/seed/metadata.json new file mode 100644 index 000000000..d5c44029d --- /dev/null +++ b/packages/data_table/seed/metadata.json @@ -0,0 +1,12 @@ +{ + "packageId": "'$pkg'", + "name": "'$(echo $pkg | sed 's/_/ /g' | awk '{for(i=1;i<=NF;i++)sub(/./,toupper(substr($i,1,1)),$i)}1')'", + "version": "0.1.0", + "description": "Package '$pkg'", + "author": "MetaBuilder Team", + "category": "ui", + "exports": { + "components": [] + }, + "dependencies": [] +} diff --git a/packages/form_builder/seed/metadata.json b/packages/form_builder/seed/metadata.json new file mode 100644 index 000000000..d5c44029d --- /dev/null +++ b/packages/form_builder/seed/metadata.json @@ -0,0 +1,12 @@ +{ + "packageId": "'$pkg'", + "name": "'$(echo $pkg | sed 's/_/ /g' | awk '{for(i=1;i<=NF;i++)sub(/./,toupper(substr($i,1,1)),$i)}1')'", + "version": "0.1.0", + "description": "Package '$pkg'", + "author": "MetaBuilder Team", + "category": "ui", + "exports": { + "components": [] + }, + "dependencies": [] +} diff --git a/packages/nav_menu/seed/metadata.json b/packages/nav_menu/seed/metadata.json new file mode 100644 index 000000000..d5c44029d --- /dev/null +++ b/packages/nav_menu/seed/metadata.json @@ -0,0 +1,12 @@ +{ + "packageId": "'$pkg'", + "name": "'$(echo $pkg | sed 's/_/ /g' | awk '{for(i=1;i<=NF;i++)sub(/./,toupper(substr($i,1,1)),$i)}1')'", + "version": "0.1.0", + "description": "Package '$pkg'", + "author": "MetaBuilder Team", + "category": "ui", + "exports": { + "components": [] + }, + "dependencies": [] +} diff --git a/packages/notification_center/seed/metadata.json b/packages/notification_center/seed/metadata.json new file mode 100644 index 000000000..d5c44029d --- /dev/null +++ b/packages/notification_center/seed/metadata.json @@ -0,0 +1,12 @@ +{ + "packageId": "'$pkg'", + "name": "'$(echo $pkg | sed 's/_/ /g' | awk '{for(i=1;i<=NF;i++)sub(/./,toupper(substr($i,1,1)),$i)}1')'", + "version": "0.1.0", + "description": "Package '$pkg'", + "author": "MetaBuilder Team", + "category": "ui", + "exports": { + "components": [] + }, + "dependencies": [] +} diff --git a/packages/ui_dialogs/seed/metadata.json b/packages/ui_dialogs/seed/metadata.json new file mode 100644 index 000000000..d5c44029d --- /dev/null +++ b/packages/ui_dialogs/seed/metadata.json @@ -0,0 +1,12 @@ +{ + "packageId": "'$pkg'", + "name": "'$(echo $pkg | sed 's/_/ /g' | awk '{for(i=1;i<=NF;i++)sub(/./,toupper(substr($i,1,1)),$i)}1')'", + "version": "0.1.0", + "description": "Package '$pkg'", + "author": "MetaBuilder Team", + "category": "ui", + "exports": { + "components": [] + }, + "dependencies": [] +} diff --git a/packages/ui_permissions/seed/metadata.json b/packages/ui_permissions/seed/metadata.json new file mode 100644 index 000000000..d5c44029d --- /dev/null +++ b/packages/ui_permissions/seed/metadata.json @@ -0,0 +1,12 @@ +{ + "packageId": "'$pkg'", + "name": "'$(echo $pkg | sed 's/_/ /g' | awk '{for(i=1;i<=NF;i++)sub(/./,toupper(substr($i,1,1)),$i)}1')'", + "version": "0.1.0", + "description": "Package '$pkg'", + "author": "MetaBuilder Team", + "category": "ui", + "exports": { + "components": [] + }, + "dependencies": [] +} diff --git a/storybook/package.json b/storybook/package.json index 4a303d1d4..015fa1feb 100644 --- a/storybook/package.json +++ b/storybook/package.json @@ -26,7 +26,7 @@ "@types/react-dom": "^18.3.5", "@vitejs/plugin-react": "^4.5.2", "sass": "^1.97.1", - "storybook": "^8.6.15", + "storybook": "^10.1.11", "typescript": "^5.9.3", "vite": "^6.3.5" }