diff --git a/app/src/api/admin.ts b/app/src/api/admin.ts index 3b7d655..de9d72b 100644 --- a/app/src/api/admin.ts +++ b/app/src/api/admin.ts @@ -1,15 +1,17 @@ -import { apiCall } from './api'; -import type { User } from '../types/authApi'; -import { API_BASE_URL, isUser } from '../types/authApi'; -import type { - CreateUserRequest, - SystemStats, - UpdateUserRequest} from '@/types/adminApi'; import { - isSystemStats -} from '@/types/adminApi'; -import type { Workspace } from '@/types/workspace'; -import { isWorkspace } from '@/types/workspace'; + API_BASE_URL, + type CreateUserRequest, + type UpdateUserRequest, +} from '@/types/api'; +import { apiCall } from './api'; +import { + isSystemStats, + isUser, + isWorkspace, + type SystemStats, + type User, + type Workspace, +} from '@/types/models'; const ADMIN_BASE_URL = `${API_BASE_URL}/admin`; @@ -22,7 +24,7 @@ const ADMIN_BASE_URL = `${API_BASE_URL}/admin`; * */ export const getUsers = async (): Promise => { const response = await apiCall(`${ADMIN_BASE_URL}/users`); - const data = await response.json(); + const data: unknown = await response.json(); if (!Array.isArray(data)) { throw new Error('Invalid users response received from API'); @@ -49,7 +51,7 @@ export const createUser = async ( body: JSON.stringify(userData), }); - const data = await response.json(); + const data: unknown = await response.json(); if (!isUser(data)) { throw new Error('Invalid user object received from API'); } @@ -88,7 +90,7 @@ export const updateUser = async ( body: JSON.stringify(userData), }); - const data = await response.json(); + const data: unknown = await response.json(); if (!isUser(data)) { throw new Error('Invalid user object received from API'); } @@ -104,7 +106,7 @@ export const updateUser = async ( * */ export const getWorkspaces = async (): Promise => { const response = await apiCall(`${ADMIN_BASE_URL}/workspaces`); - const data = await response.json(); + const data: unknown = await response.json(); if (!Array.isArray(data)) { throw new Error('Invalid workspaces response received from API'); } @@ -125,7 +127,7 @@ export const getWorkspaces = async (): Promise => { * */ export const getSystemStats = async (): Promise => { const response = await apiCall(`${ADMIN_BASE_URL}/stats`); - const data = await response.json(); + const data: unknown = await response.json(); if (!isSystemStats(data)) { throw new Error('Invalid system stats response received from API'); } diff --git a/app/src/api/auth.ts b/app/src/api/auth.ts index 3aefe78..931bd68 100644 --- a/app/src/api/auth.ts +++ b/app/src/api/auth.ts @@ -1,6 +1,6 @@ -import type { User, LoginRequest} from '../types/authApi'; -import { API_BASE_URL, isUser } from '../types/authApi'; +import { API_BASE_URL, isLoginResponse, type LoginRequest } from '@/types/api'; import { apiCall } from './api'; +import { isUser, type User } from '@/types/models'; /** * Logs in a user with email and password @@ -17,8 +17,8 @@ export const login = async (email: string, password: string): Promise => { body: JSON.stringify(loginData), }); - const data = await response.json(); - if (!('user' in data) || !isUser(data.user)) { + const data: unknown = await response.json(); + if (!isLoginResponse(data)) { throw new Error('Invalid login response from API'); } @@ -52,7 +52,7 @@ export const refreshToken = async (): Promise => { }); return true; - } catch (error) { + } catch (_error) { return false; } }; @@ -65,7 +65,7 @@ export const refreshToken = async (): Promise => { */ export const getCurrentUser = async (): Promise => { const response = await apiCall(`${API_BASE_URL}/auth/me`); - const data = await response.json(); + const data: unknown = await response.json(); if (!isUser(data)) { throw new Error('Invalid user data received from API'); diff --git a/app/src/api/file.ts b/app/src/api/file.ts index 4782bdb..08f4c4f 100644 --- a/app/src/api/file.ts +++ b/app/src/api/file.ts @@ -1,14 +1,11 @@ -import { API_BASE_URL } from '@/types/authApi'; +import { isFileNode, type FileNode } from '@/types/models'; import { apiCall } from './api'; -import type { - FileNode, - LookupResponse, - SaveFileResponse} from '@/types/fileApi'; import { - isFileNode, + API_BASE_URL, isLookupResponse, - isSaveFileResponse -} from '@/types/fileApi'; + isSaveFileResponse, + type SaveFileResponse, +} from '@/types/api'; /** * listFiles fetches the list of files in a workspace @@ -20,7 +17,7 @@ export const listFiles = async (workspaceName: string): Promise => { const response = await apiCall( `${API_BASE_URL}/workspaces/${encodeURIComponent(workspaceName)}/files` ); - const data = await response.json(); + const data: unknown = await response.json(); if (!Array.isArray(data)) { throw new Error('Invalid files response received from API'); } @@ -48,7 +45,7 @@ export const lookupFileByName = async ( workspaceName )}/files/lookup?filename=${encodeURIComponent(filename)}` ); - const data = await response.json(); + const data: unknown = await response.json(); if (!isLookupResponse(data)) { throw new Error('Invalid lookup response received from API'); } @@ -100,7 +97,7 @@ export const saveFile = async ( body: content, } ); - const data = await response.json(); + const data: unknown = await response.json(); if (!isSaveFileResponse(data)) { throw new Error('Invalid save file response received from API'); } @@ -136,11 +133,15 @@ export const getLastOpenedFile = async ( const response = await apiCall( `${API_BASE_URL}/workspaces/${encodeURIComponent(workspaceName)}/files/last` ); - const data = await response.json(); - if (!('lastOpenedFilePath' in data)) { + const data: unknown = await response.json(); + if ( + typeof data !== 'object' || + data === null || + !('lastOpenedFilePath' in data) + ) { throw new Error('Invalid last opened file response received from API'); } - return data.lastOpenedFilePath; + return data.lastOpenedFilePath as string; }; /** diff --git a/app/src/api/git.ts b/app/src/api/git.ts index 8227adf..6866d5f 100644 --- a/app/src/api/git.ts +++ b/app/src/api/git.ts @@ -1,6 +1,6 @@ -import { API_BASE_URL } from '@/types/authApi'; +import { API_BASE_URL } from '@/types/api'; import { apiCall } from './api'; -import type { CommitHash } from '@/types/git'; +import type { CommitHash } from '@/types/models'; /** * pullChanges fetches the latest changes from the remote repository @@ -14,11 +14,11 @@ export const pullChanges = async (workspaceName: string): Promise => { method: 'POST', } ); - const data = await response.json(); - if (!('message' in data)) { + const data: unknown = await response.json(); + if (typeof data !== 'object' || data === null || !('message' in data)) { throw new Error('Invalid pull response received from API'); } - return data.message; + return data.message as string; }; /** @@ -42,8 +42,8 @@ export const commitAndPush = async ( body: JSON.stringify({ message }), } ); - const data = await response.json(); - if (!('commitHash' in data)) { + const data: unknown = await response.json(); + if (typeof data !== 'object' || data === null || !('commitHash' in data)) { throw new Error('Invalid commit response received from API'); } return data.commitHash as CommitHash; diff --git a/app/src/api/user.ts b/app/src/api/user.ts index 41f09f8..9413b53 100644 --- a/app/src/api/user.ts +++ b/app/src/api/user.ts @@ -1,7 +1,6 @@ -import type { User } from '@/types/authApi'; -import { API_BASE_URL, isUser } from '@/types/authApi'; +import { API_BASE_URL, type UpdateProfileRequest } from '@/types/api'; +import { isUser, type User } from '@/types/models'; import { apiCall } from './api'; -import type { UpdateProfileRequest } from '@/types/userApi'; /** * updateProfile updates the user's profile information. @@ -16,12 +15,12 @@ export const updateProfile = async ( method: 'PUT', body: JSON.stringify(updateRequest), }); - const data = response.json(); + const data: unknown = response.json(); if (!isUser(data)) { throw new Error('Invalid user data'); } - return data as User; + return data; }; /** diff --git a/app/src/api/workspace.ts b/app/src/api/workspace.ts index 301ff48..de45cd0 100644 --- a/app/src/api/workspace.ts +++ b/app/src/api/workspace.ts @@ -1,7 +1,6 @@ -import { API_BASE_URL } from '@/types/authApi'; +import { type Workspace, isWorkspace } from '@/types/models'; import { apiCall } from './api'; -import type { Workspace } from '@/types/workspace'; -import { isWorkspace } from '@/types/workspace'; +import { API_BASE_URL } from '@/types/api'; /** * listWorkspaces fetches the list of workspaces @@ -10,7 +9,7 @@ import { isWorkspace } from '@/types/workspace'; */ export const listWorkspaces = async (): Promise => { const response = await apiCall(`${API_BASE_URL}/workspaces`); - const data = await response.json(); + const data: unknown = await response.json(); if (!Array.isArray(data)) { throw new Error('Invalid workspaces response received from API'); } @@ -36,7 +35,7 @@ export const createWorkspace = async (name: string): Promise => { }, body: JSON.stringify({ name }), }); - const data = await response.json(); + const data: unknown = await response.json(); if (!isWorkspace(data)) { throw new Error('Invalid workspace object received from API'); } @@ -105,8 +104,12 @@ export const deleteWorkspace = async ( method: 'DELETE', } ); - const data = await response.json(); - if (!('nextWorkspaceName' in data)) { + const data: unknown = await response.json(); + if ( + typeof data !== 'object' || + data === null || + !('nextWorkspaceName' in data) + ) { throw new Error('Invalid delete workspace response received from API'); } return data.nextWorkspaceName as string; @@ -119,8 +122,12 @@ export const deleteWorkspace = async ( */ export const getLastWorkspaceName = async (): Promise => { const response = await apiCall(`${API_BASE_URL}/workspaces/last`); - const data = await response.json(); - if (!('lastWorkspaceName' in data)) { + const data: unknown = await response.json(); + if ( + typeof data !== 'object' || + data === null || + !('lastWorkspaceName' in data) + ) { throw new Error('Invalid last workspace name response received from API'); } return data.lastWorkspaceName as string; diff --git a/app/src/contexts/ModalContext.tsx b/app/src/contexts/ModalContext.tsx index 30a4be4..fb5bcc4 100644 --- a/app/src/contexts/ModalContext.tsx +++ b/app/src/contexts/ModalContext.tsx @@ -1,5 +1,9 @@ -import type { ReactNode } from 'react'; -import React, { createContext, useContext, useState } from 'react'; +import React, { + type ReactNode, + createContext, + useContext, + useState, +} from 'react'; interface ModalContextType { newFileModalVisible: boolean; diff --git a/app/src/types/adminApi.ts b/app/src/types/adminApi.ts deleted file mode 100644 index 32a9b2e..0000000 --- a/app/src/types/adminApi.ts +++ /dev/null @@ -1,60 +0,0 @@ -import type { UserRole } from './authApi'; - -// CreateUserRequest holds the request fields for creating a new user -export interface CreateUserRequest { - email: string; - displayName: string; - password: string; - role: UserRole; -} - -// UpdateUserRequest holds the request fields for updating a user -export interface UpdateUserRequest { - email?: string; - displayName?: string; - password?: string; - role?: UserRole; -} - -// WorkspaceStats holds workspace statistics -export interface WorkspaceStats { - userID: number; - userEmail: string; - workspaceID: number; - workspaceName: string; - workspaceCreatedAt: string; // Using ISO string format for time.Time - fileCountStats?: FileCountStats; -} - -// Define FileCountStats based on the Go struct definition of storage.FileCountStats -export interface FileCountStats { - totalFiles: number; - totalSize: number; -} - -export interface UserStats { - totalUsers: number; - totalWorkspaces: number; - activeUsers: number; // Users with activity in last 30 days -} - -// SystemStats holds system-wide statistics -export interface SystemStats extends FileCountStats, UserStats {} - -// isSystemStats checks if the given object is a valid SystemStats object -export function isSystemStats(obj: unknown): obj is SystemStats { - return ( - typeof obj === 'object' && - obj !== null && - 'totalUsers' in obj && - typeof (obj as SystemStats).totalUsers === 'number' && - 'totalWorkspaces' in obj && - typeof (obj as SystemStats).totalWorkspaces === 'number' && - 'activeUsers' in obj && - typeof (obj as SystemStats).activeUsers === 'number' && - 'totalFiles' in obj && - typeof (obj as SystemStats).totalFiles === 'number' && - 'totalSize' in obj && - typeof (obj as SystemStats).totalSize === 'number' - ); -} diff --git a/app/src/types/api.ts b/app/src/types/api.ts new file mode 100644 index 0000000..047628d --- /dev/null +++ b/app/src/types/api.ts @@ -0,0 +1,116 @@ +import { isUser, type User, type UserRole } from './models'; + +declare global { + interface Window { + API_BASE_URL: string; + } +} + +export const API_BASE_URL = window.API_BASE_URL; + +/** + * Error response from the API + */ +export interface ErrorResponse { + message: string; +} + +/** + * API call options extending the standard RequestInit + */ +export interface ApiCallOptions extends RequestInit { + headers?: HeadersInit; +} + +/** + * Login request parameters + */ +export interface LoginRequest { + email: string; + password: string; +} + +export interface LoginResponse { + user: User; + sessionId?: string; + expiresAt?: string; // ISO 8601 string representation of the date +} + +export function isLoginResponse(obj: unknown): obj is LoginResponse { + return ( + typeof obj === 'object' && + obj !== null && + 'user' in obj && + isUser(obj.user) && + 'sessionId' in obj && + typeof (obj as LoginResponse).sessionId === 'string' && + 'expiresAt' in obj && + typeof (obj as LoginResponse).expiresAt === 'string' + ); +} + +// CreateUserRequest holds the request fields for creating a new user +export interface CreateUserRequest { + email: string; + displayName: string; + password: string; + role: UserRole; +} + +// UpdateUserRequest holds the request fields for updating a user +export interface UpdateUserRequest { + email?: string; + displayName?: string; + password?: string; + role?: UserRole; +} + +export interface LookupResponse { + paths: string[]; +} + +export function isLookupResponse(obj: unknown): obj is LookupResponse { + return ( + typeof obj === 'object' && + obj !== null && + 'paths' in obj && + Array.isArray((obj as LookupResponse).paths) && + (obj as LookupResponse).paths.every((path) => typeof path === 'string') + ); +} + +export interface SaveFileResponse { + filePath: string; + size: number; + updatedAt: string; // ISO 8601 string representation of the date +} + +export function isSaveFileResponse(obj: unknown): obj is SaveFileResponse { + return ( + typeof obj === 'object' && + obj !== null && + 'filePath' in obj && + typeof (obj as SaveFileResponse).filePath === 'string' && + 'size' in obj && + typeof (obj as SaveFileResponse).size === 'number' && + 'updatedAt' in obj && + typeof (obj as SaveFileResponse).updatedAt === 'string' + ); +} + +export interface UpdateLastOpenedFileRequest { + filePath: string; +} + +// UpdateProfileRequest represents a user profile update request +export interface UpdateProfileRequest { + displayName?: string; + email?: string; + currentPassword?: string; + newPassword?: string; +} + +// DeleteAccountRequest represents a user account deletion request +export interface DeleteAccountRequest { + password: string; +} diff --git a/app/src/types/authApi.ts b/app/src/types/authApi.ts deleted file mode 100644 index 3e40dce..0000000 --- a/app/src/types/authApi.ts +++ /dev/null @@ -1,80 +0,0 @@ -declare global { - interface Window { - API_BASE_URL: string; - } -} - -export const API_BASE_URL = window.API_BASE_URL; - -/** - * User role in the system - */ -export enum UserRole { - Admin = 'admin', - Editor = 'editor', - Viewer = 'viewer', -} - -/** - * Type guard to check if a value is a valid UserRole - */ -export function isUserRole(value: unknown): value is UserRole { - return typeof value === 'string' && value in UserRole; -} - -/** - * User model from the API - */ -export interface User { - id: number; - email: string; - displayName?: string; - role: UserRole; - createdAt: string; - lastWorkspaceId: number; -} - -/** - * Type guard to check if a value is a valid User - */ -export function isUser(value: unknown): value is User { - return ( - typeof value === 'object' && - value !== null && - 'id' in value && - typeof (value as User).id === 'number' && - 'email' in value && - typeof (value as User).email === 'string' && - ('displayName' in value - ? typeof (value as User).displayName === 'string' - : true) && - 'role' in value && - isUserRole((value as User).role) && - 'createdAt' in value && - typeof (value as User).createdAt === 'string' && - 'lastWorkspaceId' in value && - typeof (value as User).lastWorkspaceId === 'number' - ); -} - -/** - * Error response from the API - */ -export interface ErrorResponse { - message: string; -} - -/** - * Login request parameters - */ -export interface LoginRequest { - email: string; - password: string; -} - -/** - * API call options extending the standard RequestInit - */ -export interface ApiCallOptions extends RequestInit { - headers?: HeadersInit; -} diff --git a/app/src/types/file.ts b/app/src/types/file.ts deleted file mode 100644 index abfda40..0000000 --- a/app/src/types/file.ts +++ /dev/null @@ -1,37 +0,0 @@ -export enum FileAction { - Create = 'create', - Update = 'update', - Delete = 'delete', - Rename = 'rename', -} - -export enum FileExtension { - Markdown = '.md', - JPG = '.jpg', - JPEG = '.jpeg', - PNG = '.png', - GIF = '.gif', - WebP = '.webp', - SVG = '.svg', -} - -export const IMAGE_EXTENSIONS = [ - FileExtension.JPG, - FileExtension.JPEG, - FileExtension.PNG, - FileExtension.GIF, - FileExtension.WebP, - FileExtension.SVG, -]; - -export interface DefaultFile { - name: string; - path: string; - content: string; -} - -export const DEFAULT_FILE: DefaultFile = { - name: 'New File.md', - path: 'New File.md', - content: '# Welcome to NovaMD\n\nStart editing here!', -}; diff --git a/app/src/types/fileApi.ts b/app/src/types/fileApi.ts deleted file mode 100644 index 70d4261..0000000 --- a/app/src/types/fileApi.ts +++ /dev/null @@ -1,58 +0,0 @@ -export interface LookupResponse { - paths: string[]; -} - -export function isLookupResponse(obj: unknown): obj is LookupResponse { - return ( - typeof obj === 'object' && - obj !== null && - 'paths' in obj && - Array.isArray((obj as LookupResponse).paths) && - (obj as LookupResponse).paths.every((path) => typeof path === 'string') - ); -} - -export interface SaveFileResponse { - filePath: string; - size: number; - updatedAt: string; // ISO 8601 string representation of the date -} - -export function isSaveFileResponse(obj: unknown): obj is SaveFileResponse { - return ( - typeof obj === 'object' && - obj !== null && - 'filePath' in obj && - typeof (obj as SaveFileResponse).filePath === 'string' && - 'size' in obj && - typeof (obj as SaveFileResponse).size === 'number' && - 'updatedAt' in obj && - typeof (obj as SaveFileResponse).updatedAt === 'string' - ); -} - -export interface UpdateLastOpenedFileRequest { - filePath: string; -} - -export interface FileNode { - id: string; - name: string; - path: string; - children: FileNode[]; -} - -export function isFileNode(obj: unknown): obj is FileNode { - return ( - typeof obj === 'object' && - obj !== null && - 'id' in obj && - typeof (obj as FileNode).id === 'string' && - 'name' in obj && - typeof (obj as FileNode).name === 'string' && - 'path' in obj && - typeof (obj as FileNode).path === 'string' && - 'children' in obj && - Array.isArray((obj as FileNode).children) - ); -} diff --git a/app/src/types/git.ts b/app/src/types/git.ts deleted file mode 100644 index 58fc90e..0000000 --- a/app/src/types/git.ts +++ /dev/null @@ -1 +0,0 @@ -export type CommitHash = string; diff --git a/app/src/types/markdown.ts b/app/src/types/markdown.ts deleted file mode 100644 index e36c59a..0000000 --- a/app/src/types/markdown.ts +++ /dev/null @@ -1,14 +0,0 @@ -export enum InlineContainerType { - Paragraph = 'paragraph', - ListItem = 'listItem', - TableCell = 'tableCell', - Blockquote = 'blockquote', - Heading = 'heading', - Emphasis = 'emphasis', - Strong = 'strong', - Delete = 'delete', -} - -export const MARKDOWN_REGEX = { - WIKILINK: /(!?)\[\[(.*?)\]\]/g, -} as const; diff --git a/app/src/types/modal.ts b/app/src/types/modal.ts deleted file mode 100644 index 984334e..0000000 --- a/app/src/types/modal.ts +++ /dev/null @@ -1,5 +0,0 @@ -export enum ModalType { - NewFile = 'newFile', - DeleteFile = 'deleteFile', - CommitMessage = 'commitMessage', -} diff --git a/app/src/types/models.ts b/app/src/types/models.ts new file mode 100644 index 0000000..8041cb8 --- /dev/null +++ b/app/src/types/models.ts @@ -0,0 +1,269 @@ +/** + * User model from the API + */ +export interface User { + id: number; + email: string; + displayName?: string; + role: UserRole; + createdAt: string; + lastWorkspaceId: number; +} + +/** + * Type guard to check if a value is a valid User + */ +export function isUser(value: unknown): value is User { + return ( + typeof value === 'object' && + value !== null && + 'id' in value && + typeof (value as User).id === 'number' && + 'email' in value && + typeof (value as User).email === 'string' && + ('displayName' in value + ? typeof (value as User).displayName === 'string' + : true) && + 'role' in value && + isUserRole((value as User).role) && + 'createdAt' in value && + typeof (value as User).createdAt === 'string' && + 'lastWorkspaceId' in value && + typeof (value as User).lastWorkspaceId === 'number' + ); +} + +/** + * User role in the system + */ +export enum UserRole { + Admin = 'admin', + Editor = 'editor', + Viewer = 'viewer', +} + +/** + * Type guard to check if a value is a valid UserRole + */ +export function isUserRole(value: unknown): value is UserRole { + return typeof value === 'string' && value in UserRole; +} + +export enum Theme { + Light = 'light', + Dark = 'dark', +} + +export interface WorkspaceSettings { + theme: Theme; + autoSave: boolean; + showHiddenFiles: boolean; + gitEnabled: boolean; + gitUrl: string; + gitUser: string; + gitToken: string; + gitAutoCommit: boolean; + gitCommitMsgTemplate: string; + gitCommitName: string; + gitCommitEmail: string; +} + +export const DEFAULT_WORKSPACE_SETTINGS: WorkspaceSettings = { + theme: Theme.Light, + autoSave: false, + showHiddenFiles: false, + gitEnabled: false, + gitUrl: '', + gitUser: '', + gitToken: '', + gitAutoCommit: false, + gitCommitMsgTemplate: '${action} ${filename}', + gitCommitName: '', + gitCommitEmail: '', +}; + +export interface Workspace extends WorkspaceSettings { + name: string; + createdAt: number; +} + +export const DEFAULT_WORKSPACE: Workspace = { + name: '', + createdAt: Date.now(), + ...DEFAULT_WORKSPACE_SETTINGS, +}; + +export function isWorkspace(obj: unknown): obj is Workspace { + return ( + typeof obj === 'object' && + obj !== null && + 'name' in obj && + typeof (obj as Workspace).name === 'string' && + 'theme' in obj && + typeof (obj as Workspace).theme === 'string' && + 'autoSave' in obj && + typeof (obj as Workspace).autoSave === 'boolean' && + 'gitEnabled' in obj && + typeof (obj as Workspace).gitEnabled === 'boolean' && + 'gitUrl' in obj && + typeof (obj as Workspace).gitUrl === 'string' && + 'gitUser' in obj && + typeof (obj as Workspace).gitUser === 'string' && + 'gitToken' in obj && + typeof (obj as Workspace).gitToken === 'string' && + 'gitAutoCommit' in obj && + typeof (obj as Workspace).gitAutoCommit === 'boolean' && + 'gitCommitMsgTemplate' in obj && + typeof (obj as Workspace).gitCommitMsgTemplate === 'string' + ); +} + +export enum FileAction { + Create = 'create', + Update = 'update', + Delete = 'delete', + Rename = 'rename', +} + +export enum FileExtension { + Markdown = '.md', + JPG = '.jpg', + JPEG = '.jpeg', + PNG = '.png', + GIF = '.gif', + WebP = '.webp', + SVG = '.svg', +} + +export const IMAGE_EXTENSIONS = [ + FileExtension.JPG, + FileExtension.JPEG, + FileExtension.PNG, + FileExtension.GIF, + FileExtension.WebP, + FileExtension.SVG, +]; + +export interface DefaultFile { + name: string; + path: string; + content: string; +} + +export const DEFAULT_FILE: DefaultFile = { + name: 'New File.md', + path: 'New File.md', + content: '# Welcome to NovaMD\n\nStart editing here!', +}; + +export interface FileNode { + id: string; + name: string; + path: string; + children: FileNode[]; +} + +export function isFileNode(obj: unknown): obj is FileNode { + return ( + typeof obj === 'object' && + obj !== null && + 'id' in obj && + typeof (obj as FileNode).id === 'string' && + 'name' in obj && + typeof (obj as FileNode).name === 'string' && + 'path' in obj && + typeof (obj as FileNode).path === 'string' && + 'children' in obj && + Array.isArray((obj as FileNode).children) + ); +} + +// WorkspaceStats holds workspace statistics +export interface WorkspaceStats { + userID: number; + userEmail: string; + workspaceID: number; + workspaceName: string; + workspaceCreatedAt: string; // Using ISO string format for time.Time + fileCountStats?: FileCountStats; +} + +// Define FileCountStats based on the Go struct definition of storage.FileCountStats +export interface FileCountStats { + totalFiles: number; + totalSize: number; +} + +export interface UserStats { + totalUsers: number; + totalWorkspaces: number; + activeUsers: number; // Users with activity in last 30 days +} + +// SystemStats holds system-wide statistics +export interface SystemStats extends FileCountStats, UserStats {} + +// isSystemStats checks if the given object is a valid SystemStats object +export function isSystemStats(obj: unknown): obj is SystemStats { + return ( + typeof obj === 'object' && + obj !== null && + 'totalUsers' in obj && + typeof (obj as SystemStats).totalUsers === 'number' && + 'totalWorkspaces' in obj && + typeof (obj as SystemStats).totalWorkspaces === 'number' && + 'activeUsers' in obj && + typeof (obj as SystemStats).activeUsers === 'number' && + 'totalFiles' in obj && + typeof (obj as SystemStats).totalFiles === 'number' && + 'totalSize' in obj && + typeof (obj as SystemStats).totalSize === 'number' + ); +} + +export type CommitHash = string; + +export enum InlineContainerType { + Paragraph = 'paragraph', + ListItem = 'listItem', + TableCell = 'tableCell', + Blockquote = 'blockquote', + Heading = 'heading', + Emphasis = 'emphasis', + Strong = 'strong', + Delete = 'delete', +} + +export const MARKDOWN_REGEX = { + WIKILINK: /(!?)\[\[(.*?)\]\]/g, +} as const; + +export enum ModalType { + NewFile = 'newFile', + DeleteFile = 'deleteFile', + CommitMessage = 'commitMessage', +} + +export enum SettingsActionType { + INIT_SETTINGS = 'INIT_SETTINGS', + UPDATE_LOCAL_SETTINGS = 'UPDATE_LOCAL_SETTINGS', + MARK_SAVED = 'MARK_SAVED', +} + +export interface UserProfileSettings { + displayName?: string; + email?: string; + currentPassword?: string; + newPassword?: string; +} + +export interface ProfileSettingsState { + localSettings: UserProfileSettings; + initialSettings: UserProfileSettings; + hasUnsavedChanges: boolean; +} + +export interface SettingsAction { + type: SettingsActionType; + payload?: T; +} diff --git a/app/src/types/settings.ts b/app/src/types/settings.ts deleted file mode 100644 index 33d5122..0000000 --- a/app/src/types/settings.ts +++ /dev/null @@ -1,23 +0,0 @@ -export enum SettingsActionType { - INIT_SETTINGS = 'INIT_SETTINGS', - UPDATE_LOCAL_SETTINGS = 'UPDATE_LOCAL_SETTINGS', - MARK_SAVED = 'MARK_SAVED', -} - -export interface UserProfileSettings { - displayName?: string; - email?: string; - currentPassword?: string; - newPassword?: string; -} - -export interface ProfileSettingsState { - localSettings: UserProfileSettings; - initialSettings: UserProfileSettings; - hasUnsavedChanges: boolean; -} - -export interface SettingsAction { - type: SettingsActionType; - payload?: T; -} diff --git a/app/src/types/theme.ts b/app/src/types/theme.ts deleted file mode 100644 index b6f3c1e..0000000 --- a/app/src/types/theme.ts +++ /dev/null @@ -1,4 +0,0 @@ -export enum Theme { - Light = 'light', - Dark = 'dark', -} diff --git a/app/src/types/userApi.ts b/app/src/types/userApi.ts deleted file mode 100644 index 2cec49d..0000000 --- a/app/src/types/userApi.ts +++ /dev/null @@ -1,12 +0,0 @@ -// UpdateProfileRequest represents a user profile update request -export interface UpdateProfileRequest { - displayName?: string; - email?: string; - currentPassword?: string; - newPassword?: string; -} - -// DeleteAccountRequest represents a user account deletion request -export interface DeleteAccountRequest { - password: string; -} diff --git a/app/src/types/workspace.ts b/app/src/types/workspace.ts deleted file mode 100644 index 4d8f359..0000000 --- a/app/src/types/workspace.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { Theme } from './theme'; - -export interface WorkspaceSettings { - theme: Theme; - autoSave: boolean; - showHiddenFiles: boolean; - gitEnabled: boolean; - gitUrl: string; - gitUser: string; - gitToken: string; - gitAutoCommit: boolean; - gitCommitMsgTemplate: string; - gitCommitName: string; - gitCommitEmail: string; -} - -export const DEFAULT_WORKSPACE_SETTINGS: WorkspaceSettings = { - theme: Theme.Light, - autoSave: false, - showHiddenFiles: false, - gitEnabled: false, - gitUrl: '', - gitUser: '', - gitToken: '', - gitAutoCommit: false, - gitCommitMsgTemplate: '${action} ${filename}', - gitCommitName: '', - gitCommitEmail: '', -}; - -export interface Workspace extends WorkspaceSettings { - name: string; - createdAt: number; -} - -export const DEFAULT_WORKSPACE: Workspace = { - name: '', - createdAt: Date.now(), - ...DEFAULT_WORKSPACE_SETTINGS, -}; - -export function isWorkspace(obj: unknown): obj is Workspace { - return ( - typeof obj === 'object' && - obj !== null && - 'name' in obj && - typeof (obj as Workspace).name === 'string' && - 'theme' in obj && - typeof (obj as Workspace).theme === 'string' && - 'autoSave' in obj && - typeof (obj as Workspace).autoSave === 'boolean' && - 'gitEnabled' in obj && - typeof (obj as Workspace).gitEnabled === 'boolean' && - 'gitUrl' in obj && - typeof (obj as Workspace).gitUrl === 'string' && - 'gitUser' in obj && - typeof (obj as Workspace).gitUser === 'string' && - 'gitToken' in obj && - typeof (obj as Workspace).gitToken === 'string' && - 'gitAutoCommit' in obj && - typeof (obj as Workspace).gitAutoCommit === 'boolean' && - 'gitCommitMsgTemplate' in obj && - typeof (obj as Workspace).gitCommitMsgTemplate === 'string' - ); -}