diff --git a/src/App.tsx b/src/App.tsx
deleted file mode 100644
index 6113f4a..0000000
--- a/src/App.tsx
+++ /dev/null
@@ -1,93 +0,0 @@
-import { useEffect } from 'react'
-import { Routes, Route, Navigate } from 'react-router-dom'
-import { useAuth } from './hooks/useAuth'
-import { useTheme } from './hooks/useTheme'
-
-// Layout Components
-import { PublicLayout } from './components/layouts/PublicLayout'
-import { ProtectedLayout } from './components/layouts/ProtectedLayout'
-
-// Pages
-import { LoginPage } from './pages/auth/LoginPage'
-import { DashboardPage } from './pages/dashboard/DashboardPage'
-import { CertificatesPage } from './pages/certificates/CertificatesPage'
-import { CreateCertificatePage } from './pages/certificates/CreateCertificatePage'
-import { PerimetersPage } from './pages/perimeters/PerimetersPage'
-import { CreatePerimeterPage } from './pages/perimeters/CreatePerimeterPage'
-import { UsersPage } from './pages/users/UsersPage'
-import { CreateUserPage } from './pages/users/CreateUserPage'
-import { EditUserPasswordPage } from './pages/users/EditUserPasswordPage'
-import { NotFoundPage } from './pages/NotFoundPage'
-
-// Components
-import { LoadingSpinner } from './components/ui/LoadingSpinner'
-
-function App() {
- const { isAuthenticated, isLoading } = useAuth()
- const { theme } = useTheme()
-
- useEffect(() => {
- // Apply theme to document
- if (theme === 'dark') {
- document.documentElement.classList.add('dark')
- } else {
- document.documentElement.classList.remove('dark')
- }
- }, [theme])
-
- if (isLoading) {
- return (
-
-
-
- )
- }
-
- return (
-
- {/* Public Routes */}
-
-
-
- ) : (
-
- )
- } />
-
- {/* Protected Routes */}
-
- ) : (
-
- )
- }>
- } />
- } />
-
-
- } />
- } />
-
-
-
- } />
- } />
-
-
-
- } />
- } />
- } />
-
-
-
- {/* 404 */}
- } />
-
- )
-}
-
-export default App
\ No newline at end of file
diff --git a/src/components/layouts/PublicLayout.tsx b/src/components/layouts/PublicLayout.tsx
deleted file mode 100644
index 5f6bede..0000000
--- a/src/components/layouts/PublicLayout.tsx
+++ /dev/null
@@ -1,95 +0,0 @@
-import React from 'react';
-import { useI18nContext } from '../../contexts/I18nContext';
-import { useThemeContext } from '../../contexts/ThemeContext';
-
-interface PublicLayoutProps {
- children: React.ReactNode;
-}
-
-export const PublicLayout: React.FC = ({ children }) => {
- const { t, language, setLanguage } = useI18nContext();
- const { theme, toggleTheme } = useThemeContext();
-
- const supportedLanguages: Array<{ code: string; name: string }> = [
- { code: 'fr', name: 'FR' },
- { code: 'en', name: 'EN' },
- { code: 'de', name: 'DE' },
- { code: 'it', name: 'IT' },
- { code: 'pt', name: 'PT' },
- { code: 'es', name: 'ES' },
- { code: 'ja', name: '日本語' },
- { code: 'ru', name: 'RU' },
- { code: 'ar', name: 'العربية' },
- { code: 'hi', name: 'हिन्दी' },
- { code: 'zh', name: '中文' },
- ];
-
- return (
-
- {/* Header */}
-
-
-
- {/* Logo and title */}
-
-
-
- {t('app_name')}
-
-
-
-
- {/* Controls */}
-
- {/* Language selector */}
-
-
-
-
- {/* Theme toggle */}
-
-
-
-
-
-
- {/* Main content */}
-
- {children}
-
-
- {/* Footer */}
-
-
- );
-};
\ No newline at end of file
diff --git a/src/components/ui/Button.tsx b/src/components/ui/Button.tsx
deleted file mode 100644
index 9182486..0000000
--- a/src/components/ui/Button.tsx
+++ /dev/null
@@ -1,68 +0,0 @@
-import React from 'react'
-import { cn } from '../../lib/utils'
-import { LoadingSpinner } from './LoadingSpinner'
-
-interface ButtonProps extends React.ButtonHTMLAttributes {
- variant?: 'primary' | 'secondary' | 'destructive' | 'outline' | 'ghost'
- size?: 'sm' | 'md' | 'lg'
- loading?: boolean
- leftIcon?: React.ReactNode
- rightIcon?: React.ReactNode
-}
-
-export const Button = React.forwardRef(
- ({
- className,
- variant = 'primary',
- size = 'md',
- loading = false,
- leftIcon,
- rightIcon,
- children,
- disabled,
- ...props
- }, ref) => {
- const variants = {
- primary: 'btn-primary',
- secondary: 'btn-secondary',
- destructive: 'btn-destructive',
- outline: 'btn-outline',
- ghost: 'btn-ghost',
- }
-
- const sizes = {
- sm: 'btn-sm',
- md: '',
- lg: 'btn-lg',
- }
-
- return (
-
- )
- }
-)
-
-Button.displayName = 'Button'
\ No newline at end of file
diff --git a/src/components/ui/LoadingSpinner.tsx b/src/components/ui/LoadingSpinner.tsx
deleted file mode 100644
index 8538e9d..0000000
--- a/src/components/ui/LoadingSpinner.tsx
+++ /dev/null
@@ -1,32 +0,0 @@
-import React from 'react'
-import { cn } from '../../lib/utils'
-
-interface LoadingSpinnerProps {
- size?: 'sm' | 'md' | 'lg'
- className?: string
-}
-
-export const LoadingSpinner: React.FC = ({
- size = 'md',
- className
-}) => {
- const sizes = {
- sm: 'w-4 h-4',
- md: 'w-6 h-6',
- lg: 'w-8 h-8',
- }
-
- return (
-
- Loading...
-
- )
-}
\ No newline at end of file
diff --git a/src/contexts/AuthContext.tsx b/src/contexts/AuthContext.tsx
deleted file mode 100644
index 3d81728..0000000
--- a/src/contexts/AuthContext.tsx
+++ /dev/null
@@ -1,80 +0,0 @@
-import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react';
-import { User } from '../types';
-import { authApi } from '../services/api';
-
-interface AuthContextType {
- user: User | null;
- isLoading: boolean;
- login: (username: string, password: string) => Promise;
- logout: () => void;
- isAuthenticated: boolean;
-}
-
-const AuthContext = createContext(undefined);
-
-export const useAuthContext = () => {
- const context = useContext(AuthContext);
- if (context === undefined) {
- throw new Error('useAuthContext must be used within an AuthProvider');
- }
- return context;
-};
-
-interface AuthProviderProps {
- children: ReactNode;
-}
-
-export const AuthProvider: React.FC = ({ children }) => {
- const [user, setUser] = useState(null);
- const [isLoading, setIsLoading] = useState(true);
-
- useEffect(() => {
- // Check if user is already logged in
- const token = localStorage.getItem('auth_token');
- if (token) {
- // Validate token and get user info
- authApi.me()
- .then(userData => {
- setUser(userData);
- })
- .catch(() => {
- localStorage.removeItem('auth_token');
- })
- .finally(() => {
- setIsLoading(false);
- });
- } else {
- setIsLoading(false);
- }
- }, []);
-
- const login = async (username: string, password: string) => {
- setIsLoading(true);
- try {
- const response = await authApi.login({ username, password });
- localStorage.setItem('auth_token', response.token);
- setUser(response.user);
- } finally {
- setIsLoading(false);
- }
- };
-
- const logout = () => {
- localStorage.removeItem('auth_token');
- setUser(null);
- };
-
- const value: AuthContextType = {
- user,
- isLoading,
- login,
- logout,
- isAuthenticated: !!user,
- };
-
- return (
-
- {children}
-
- );
-};
\ No newline at end of file
diff --git a/src/contexts/I18nContext.tsx b/src/contexts/I18nContext.tsx
deleted file mode 100644
index 557e3e5..0000000
--- a/src/contexts/I18nContext.tsx
+++ /dev/null
@@ -1,194 +0,0 @@
-import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react';
-
-type Language = 'fr' | 'en' | 'de' | 'it' | 'pt' | 'es' | 'ja' | 'ru' | 'ar' | 'hi' | 'zh';
-
-interface I18nContextType {
- language: Language;
- setLanguage: (lang: Language) => void;
- t: (key: string, params?: Record) => string;
-}
-
-const I18nContext = createContext(undefined);
-
-export const useI18nContext = () => {
- const context = useContext(I18nContext);
- if (context === undefined) {
- throw new Error('useI18nContext must be used within an I18nProvider');
- }
- return context;
-};
-
-// Basic translations for key UI elements
-const translations: Record> = {
- fr: {
- 'app_name': 'Gestion Certificat',
- 'login': 'Connexion',
- 'logout': 'Déconnexion',
- 'username': 'Nom d\'utilisateur',
- 'password': 'Mot de passe',
- 'dashboard': 'Tableau de bord',
- 'certificates': 'Certificats',
- 'users': 'Utilisateurs',
- 'loading': 'Chargement...',
- },
- en: {
- 'app_name': 'Certificate Management',
- 'login': 'Login',
- 'logout': 'Logout',
- 'username': 'Username',
- 'password': 'Password',
- 'dashboard': 'Dashboard',
- 'certificates': 'Certificates',
- 'users': 'Users',
- 'loading': 'Loading...',
- },
- de: {
- 'app_name': 'Zertifikatsverwaltung',
- 'login': 'Anmelden',
- 'logout': 'Abmelden',
- 'username': 'Benutzername',
- 'password': 'Passwort',
- 'dashboard': 'Dashboard',
- 'certificates': 'Zertifikate',
- 'users': 'Benutzer',
- 'loading': 'Wird geladen...',
- },
- it: {
- 'app_name': 'Gestione Certificati',
- 'login': 'Accesso',
- 'logout': 'Esci',
- 'username': 'Nome utente',
- 'password': 'Password',
- 'dashboard': 'Dashboard',
- 'certificates': 'Certificati',
- 'users': 'Utenti',
- 'loading': 'Caricamento...',
- },
- pt: {
- 'app_name': 'Gestão de Certificados',
- 'login': 'Login',
- 'logout': 'Sair',
- 'username': 'Nome de Utilizador',
- 'password': 'Palavra-passe',
- 'dashboard': 'Painel de Controlo',
- 'certificates': 'Certificados',
- 'users': 'Utilizadores',
- 'loading': 'Carregando...',
- },
- es: {
- 'app_name': 'Gestión de Certificados',
- 'login': 'Iniciar Sesión',
- 'logout': 'Cerrar Sesión',
- 'username': 'Nombre de usuario',
- 'password': 'Contraseña',
- 'dashboard': 'Panel de Control',
- 'certificates': 'Certificados',
- 'users': 'Usuarios',
- 'loading': 'Cargando...',
- },
- ja: {
- 'app_name': '証明書管理',
- 'login': 'ログイン',
- 'logout': 'ログアウト',
- 'username': 'ユーザー名',
- 'password': 'パスワード',
- 'dashboard': 'ダッシュボード',
- 'certificates': '証明書',
- 'users': 'ユーザー',
- 'loading': '読み込み中...',
- },
- ru: {
- 'app_name': 'Управление Сертификатами',
- 'login': 'Вход',
- 'logout': 'Выход',
- 'username': 'Имя пользователя',
- 'password': 'Пароль',
- 'dashboard': 'Панель управления',
- 'certificates': 'Сертификаты',
- 'users': 'Пользователи',
- 'loading': 'Загрузка...',
- },
- ar: {
- 'app_name': 'إدارة الشهادات',
- 'login': 'تسجيل الدخول',
- 'logout': 'تسجيل الخروج',
- 'username': 'اسم المستخدم',
- 'password': 'كلمة المرور',
- 'dashboard': 'لوحة التحكم',
- 'certificates': 'الشهادات',
- 'users': 'المستخدمون',
- 'loading': 'جار التحميل...',
- },
- hi: {
- 'app_name': 'प्रमाणपत्र प्रबंधन',
- 'login': 'लॉगिन',
- 'logout': 'लॉगआउट',
- 'username': 'उपयोगकर्ता नाम',
- 'password': 'पासवर्ड',
- 'dashboard': 'डैशबोर्ड',
- 'certificates': 'प्रमाणपत्र',
- 'users': 'उपयोगकर्ता',
- 'loading': 'लोड हो रहा है...',
- },
- zh: {
- 'app_name': '证书管理',
- 'login': '登录',
- 'logout': '注销',
- 'username': '用户名',
- 'password': '密码',
- 'dashboard': '仪表板',
- 'certificates': '证书',
- 'users': '用户',
- 'loading': '加载中...',
- },
-};
-
-interface I18nProviderProps {
- children: ReactNode;
-}
-
-export const I18nProvider: React.FC = ({ children }) => {
- const [language, setLanguageState] = useState('fr');
-
- useEffect(() => {
- // Load language from localStorage or browser preference
- const savedLanguage = localStorage.getItem('language') as Language;
- if (savedLanguage && Object.keys(translations).includes(savedLanguage)) {
- setLanguageState(savedLanguage);
- } else {
- const browserLang = navigator.language.split('-')[0] as Language;
- if (Object.keys(translations).includes(browserLang)) {
- setLanguageState(browserLang);
- }
- }
- }, []);
-
- const setLanguage = (lang: Language) => {
- setLanguageState(lang);
- localStorage.setItem('language', lang);
- };
-
- const t = (key: string, params?: Record): string => {
- let translation = translations[language]?.[key] || key;
-
- if (params) {
- Object.entries(params).forEach(([param, value]) => {
- translation = translation.replace(`{${param}}`, value);
- });
- }
-
- return translation;
- };
-
- const value: I18nContextType = {
- language,
- setLanguage,
- t,
- };
-
- return (
-
- {children}
-
- );
-};
\ No newline at end of file
diff --git a/src/contexts/ThemeContext.tsx b/src/contexts/ThemeContext.tsx
deleted file mode 100644
index ee355b6..0000000
--- a/src/contexts/ThemeContext.tsx
+++ /dev/null
@@ -1,69 +0,0 @@
-import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react';
-
-type Theme = 'light' | 'dark';
-
-interface ThemeContextType {
- theme: Theme;
- toggleTheme: () => void;
- setTheme: (theme: Theme) => void;
-}
-
-const ThemeContext = createContext(undefined);
-
-export const useThemeContext = () => {
- const context = useContext(ThemeContext);
- if (context === undefined) {
- throw new Error('useThemeContext must be used within a ThemeProvider');
- }
- return context;
-};
-
-interface ThemeProviderProps {
- children: ReactNode;
-}
-
-export const ThemeProvider: React.FC = ({ children }) => {
- const [theme, setThemeState] = useState('light');
-
- useEffect(() => {
- // Load theme from localStorage or system preference
- const savedTheme = localStorage.getItem('theme') as Theme;
- if (savedTheme) {
- setThemeState(savedTheme);
- } else {
- const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
- setThemeState(prefersDark ? 'dark' : 'light');
- }
- }, []);
-
- useEffect(() => {
- // Apply theme to document
- const root = document.documentElement;
- if (theme === 'dark') {
- root.classList.add('dark');
- } else {
- root.classList.remove('dark');
- }
- localStorage.setItem('theme', theme);
- }, [theme]);
-
- const toggleTheme = () => {
- setThemeState(prev => prev === 'light' ? 'dark' : 'light');
- };
-
- const setTheme = (newTheme: Theme) => {
- setThemeState(newTheme);
- };
-
- const value: ThemeContextType = {
- theme,
- toggleTheme,
- setTheme,
- };
-
- return (
-
- {children}
-
- );
-};
\ No newline at end of file
diff --git a/src/hooks/useAuth.ts b/src/hooks/useAuth.ts
deleted file mode 100644
index eaa0c51..0000000
--- a/src/hooks/useAuth.ts
+++ /dev/null
@@ -1,75 +0,0 @@
-import { create } from 'zustand'
-import { persist } from 'zustand/middleware'
-import { AuthUser, LoginCredentials } from '../types'
-import { authApi } from '../services/api'
-import toast from 'react-hot-toast'
-
-interface AuthState {
- user: AuthUser | null
- isAuthenticated: boolean
- isLoading: boolean
- login: (credentials: LoginCredentials) => Promise
- logout: () => void
- updateUser: (user: Partial) => void
-}
-
-export const useAuth = create()(
- persist(
- (set, get) => ({
- user: null,
- isAuthenticated: false,
- isLoading: false,
-
- login: async (credentials: LoginCredentials) => {
- set({ isLoading: true })
- try {
- const response = await authApi.login(credentials)
- if (response.success && response.data) {
- set({
- user: response.data,
- isAuthenticated: true,
- isLoading: false,
- })
- toast.success('Successfully logged in!')
- return true
- } else {
- toast.error(response.message || 'Login failed')
- set({ isLoading: false })
- return false
- }
- } catch (error) {
- console.error('Login error:', error)
- toast.error('Login failed. Please try again.')
- set({ isLoading: false })
- return false
- }
- },
-
- logout: () => {
- authApi.logout().catch(console.error)
- set({
- user: null,
- isAuthenticated: false,
- isLoading: false,
- })
- toast.success('Successfully logged out!')
- },
-
- updateUser: (userData: Partial) => {
- const currentUser = get().user
- if (currentUser) {
- set({
- user: { ...currentUser, ...userData },
- })
- }
- },
- }),
- {
- name: 'auth-storage',
- partialize: (state) => ({
- user: state.user,
- isAuthenticated: state.isAuthenticated,
- }),
- }
- )
-)
\ No newline at end of file
diff --git a/src/hooks/useTheme.ts b/src/hooks/useTheme.ts
deleted file mode 100644
index 58696ad..0000000
--- a/src/hooks/useTheme.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-import { create } from 'zustand'
-import { persist } from 'zustand/middleware'
-import { Theme } from '../types'
-
-interface ThemeState {
- theme: Theme
- setTheme: (theme: Theme) => void
- toggleTheme: () => void
-}
-
-export const useTheme = create()(
- persist(
- (set, get) => ({
- theme: 'light',
-
- setTheme: (theme: Theme) => {
- set({ theme })
- },
-
- toggleTheme: () => {
- const currentTheme = get().theme
- set({ theme: currentTheme === 'light' ? 'dark' : 'light' })
- },
- }),
- {
- name: 'theme-storage',
- }
- )
-)
\ No newline at end of file
diff --git a/src/lib/utils.ts b/src/lib/utils.ts
deleted file mode 100644
index 87d4b24..0000000
--- a/src/lib/utils.ts
+++ /dev/null
@@ -1,64 +0,0 @@
-import { type ClassValue, clsx } from 'clsx'
-import { twMerge } from 'tailwind-merge'
-
-export function cn(...inputs: ClassValue[]) {
- return twMerge(clsx(inputs))
-}
-
-export function formatDate(date: string | Date, options?: Intl.DateTimeFormatOptions): string {
- const dateObject = typeof date === 'string' ? new Date(date) : date
-
- return new Intl.DateTimeFormat('en-US', {
- year: 'numeric',
- month: 'short',
- day: 'numeric',
- ...options,
- }).format(dateObject)
-}
-
-export function formatDateTime(date: string | Date): string {
- return formatDate(date, {
- year: 'numeric',
- month: 'short',
- day: 'numeric',
- hour: '2-digit',
- minute: '2-digit',
- })
-}
-
-export function downloadBlob(blob: Blob, filename: string): void {
- const url = window.URL.createObjectURL(blob)
- const link = document.createElement('a')
- link.href = url
- link.download = filename
- document.body.appendChild(link)
- link.click()
- document.body.removeChild(link)
- window.URL.revokeObjectURL(url)
-}
-
-export function isValidEmail(email: string): boolean {
- const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
- return emailRegex.test(email)
-}
-
-export function capitalizeFirst(str: string): string {
- return str.charAt(0).toUpperCase() + str.slice(1)
-}
-
-export function debounce any>(
- func: T,
- wait: number
-): (...args: Parameters) => void {
- let timeout: NodeJS.Timeout | null = null
-
- return (...args: Parameters) => {
- if (timeout) clearTimeout(timeout)
- timeout = setTimeout(() => func(...args), wait)
- }
-}
-
-export function truncate(str: string, length: number): string {
- if (str.length <= length) return str
- return str.slice(0, length) + '...'
-}
\ No newline at end of file
diff --git a/src/main.tsx b/src/main.tsx
deleted file mode 100644
index 71d920d..0000000
--- a/src/main.tsx
+++ /dev/null
@@ -1,49 +0,0 @@
-import React from 'react'
-import ReactDOM from 'react-dom/client'
-import { QueryClient, QueryClientProvider } from 'react-query'
-import { BrowserRouter } from 'react-router-dom'
-import { Toaster } from 'react-hot-toast'
-
-import App from './App'
-import { AuthProvider } from './contexts/AuthContext'
-import { ThemeProvider } from './contexts/ThemeContext'
-import { I18nProvider } from './contexts/I18nContext'
-
-import './styles/globals.css'
-
-const queryClient = new QueryClient({
- defaultOptions: {
- queries: {
- retry: 1,
- refetchOnWindowFocus: false,
- staleTime: 5 * 60 * 1000, // 5 minutes
- },
- },
-})
-
-ReactDOM.createRoot(document.getElementById('root')!).render(
-
-
-
-
-
-
-
-
-
-
-
-
-
- ,
-)
\ No newline at end of file
diff --git a/src/services/api/auth.ts b/src/services/api/auth.ts
deleted file mode 100644
index 7821cd3..0000000
--- a/src/services/api/auth.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import { ApiResponse, AuthUser, LoginCredentials } from '../../types'
-import { apiClient } from './client'
-
-export const authApi = {
- login: async (credentials: LoginCredentials): Promise> => {
- const response = await apiClient.post('/auth/login', credentials)
- return response.data
- },
-
- logout: async (): Promise => {
- const response = await apiClient.post('/auth/logout')
- return response.data
- },
-
- me: async (): Promise> => {
- const response = await apiClient.get('/auth/me')
- return response.data
- },
-}
\ No newline at end of file
diff --git a/src/services/api/certificates.ts b/src/services/api/certificates.ts
deleted file mode 100644
index 7fb1dc9..0000000
--- a/src/services/api/certificates.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-import {
- ApiResponse,
- Certificate,
- CreateCertificateData,
- DownloadCertificateParams,
- PaginatedResponse
-} from '../../types'
-import { apiClient } from './client'
-
-export const certificatesApi = {
- getAll: async (page = 1, perPage = 50): Promise>> => {
- const response = await apiClient.get('/certificates', {
- params: { page, per_page: perPage }
- })
- return response.data
- },
-
- create: async (data: CreateCertificateData): Promise> => {
- const response = await apiClient.post('/certificates', data)
- return response.data
- },
-
- revoke: async (certificateId: number): Promise => {
- const response = await apiClient.post(`/certificates/${certificateId}/revoke`)
- return response.data
- },
-
- download: async (params: DownloadCertificateParams): Promise => {
- const response = await apiClient.get('/certificates/download', {
- params,
- responseType: 'blob',
- })
- return response.data
- },
-
- getStats: async (): Promise> => {
- const response = await apiClient.get('/certificates/stats')
- return response.data
- },
-}
\ No newline at end of file
diff --git a/src/services/api/client.ts b/src/services/api/client.ts
deleted file mode 100644
index 6d166fc..0000000
--- a/src/services/api/client.ts
+++ /dev/null
@@ -1,57 +0,0 @@
-import axios from 'axios'
-import toast from 'react-hot-toast'
-
-const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || '/api'
-
-export const apiClient = axios.create({
- baseURL: API_BASE_URL,
- timeout: 30000,
- headers: {
- 'Content-Type': 'application/json',
- },
-})
-
-// Request interceptor
-apiClient.interceptors.request.use(
- (config) => {
- // Add auth token if available
- const authData = localStorage.getItem('auth-storage')
- if (authData) {
- try {
- const parsed = JSON.parse(authData)
- if (parsed.state?.user?.token) {
- config.headers.Authorization = `Bearer ${parsed.state.user.token}`
- }
- } catch (error) {
- console.error('Error parsing auth data:', error)
- }
- }
- return config
- },
- (error) => {
- return Promise.reject(error)
- }
-)
-
-// Response interceptor
-apiClient.interceptors.response.use(
- (response) => {
- return response
- },
- (error) => {
- if (error.response?.status === 401) {
- // Clear auth state and redirect to login
- localStorage.removeItem('auth-storage')
- window.location.href = '/login'
- toast.error('Session expired. Please login again.')
- } else if (error.response?.status === 403) {
- toast.error('You do not have permission to perform this action.')
- } else if (error.response?.status >= 500) {
- toast.error('Server error. Please try again later.')
- } else if (error.code === 'ECONNABORTED') {
- toast.error('Request timeout. Please check your connection.')
- }
-
- return Promise.reject(error)
- }
-)
\ No newline at end of file
diff --git a/src/services/api/index.ts b/src/services/api/index.ts
deleted file mode 100644
index c1d205b..0000000
--- a/src/services/api/index.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-export { authApi } from './auth'
-export { certificatesApi } from './certificates'
-export { perimetersApi } from './perimeters'
-export { usersApi } from './users'
-export { dashboardApi } from './dashboard'
-export { apiClient } from './client'
\ No newline at end of file
diff --git a/src/services/api/perimeters.ts b/src/services/api/perimeters.ts
deleted file mode 100644
index 4f726cd..0000000
--- a/src/services/api/perimeters.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-import { apiClient } from './client';
-import { FunctionalPerimeter } from '../../types';
-
-export interface CreatePerimeterRequest {
- name: string;
- intermediate_passphrase?: string;
-}
-
-export interface PerimeterResponse {
- success: boolean;
- data: FunctionalPerimeter;
- message?: string;
-}
-
-export interface PerimetersListResponse {
- success: boolean;
- data: FunctionalPerimeter[];
- message?: string;
-}
-
-export const perimetersApi = {
- async getAll(): Promise {
- const response = await apiClient.get('/perimeters');
- return response.data.data;
- },
-
- async getById(id: number): Promise {
- const response = await apiClient.get(`/perimeters/${id}`);
- return response.data.data;
- },
-
- async create(data: CreatePerimeterRequest): Promise {
- const response = await apiClient.post('/perimeters', data);
- return response.data.data;
- },
-
- async delete(id: number): Promise {
- await apiClient.delete(`/perimeters/${id}`);
- },
-};
\ No newline at end of file
diff --git a/src/styles/globals.css b/src/styles/globals.css
deleted file mode 100644
index 75052f4..0000000
--- a/src/styles/globals.css
+++ /dev/null
@@ -1,182 +0,0 @@
-@import 'tailwindcss/base';
-@import 'tailwindcss/components';
-@import 'tailwindcss/utilities';
-
-@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
-
-@layer base {
- :root {
- --background: 0 0% 100%;
- --foreground: 222.2 84% 4.9%;
- --card: 0 0% 100%;
- --card-foreground: 222.2 84% 4.9%;
- --popover: 0 0% 100%;
- --popover-foreground: 222.2 84% 4.9%;
- --primary: 221.2 83.2% 53.3%;
- --primary-foreground: 210 40% 98%;
- --secondary: 210 40% 96%;
- --secondary-foreground: 222.2 84% 4.9%;
- --muted: 210 40% 96%;
- --muted-foreground: 215.4 16.3% 46.9%;
- --accent: 210 40% 96%;
- --accent-foreground: 222.2 84% 4.9%;
- --destructive: 0 84.2% 60.2%;
- --destructive-foreground: 210 40% 98%;
- --border: 214.3 31.8% 91.4%;
- --input: 214.3 31.8% 91.4%;
- --ring: 221.2 83.2% 53.3%;
- --radius: 0.75rem;
- }
-
- .dark {
- --background: 222.2 84% 4.9%;
- --foreground: 210 40% 98%;
- --card: 222.2 84% 4.9%;
- --card-foreground: 210 40% 98%;
- --popover: 222.2 84% 4.9%;
- --popover-foreground: 210 40% 98%;
- --primary: 217.2 91.2% 59.8%;
- --primary-foreground: 222.2 84% 4.9%;
- --secondary: 217.2 32.6% 17.5%;
- --secondary-foreground: 210 40% 98%;
- --muted: 217.2 32.6% 17.5%;
- --muted-foreground: 215 20.2% 65.1%;
- --accent: 217.2 32.6% 17.5%;
- --accent-foreground: 210 40% 98%;
- --destructive: 0 62.8% 30.6%;
- --destructive-foreground: 210 40% 98%;
- --border: 217.2 32.6% 17.5%;
- --input: 217.2 32.6% 17.5%;
- --ring: 224.3 76.3% 94.1%;
- }
-}
-
-@layer base {
- * {
- @apply border-border;
- }
-
- body {
- @apply bg-background text-foreground font-sans;
- font-feature-settings: "rlig" 1, "calt" 1;
- }
-
- h1, h2, h3, h4, h5, h6 {
- @apply font-semibold tracking-tight;
- }
-
- h1 {
- @apply text-3xl lg:text-4xl;
- }
-
- h2 {
- @apply text-2xl lg:text-3xl;
- }
-
- h3 {
- @apply text-xl lg:text-2xl;
- }
-
- h4 {
- @apply text-lg lg:text-xl;
- }
-}
-
-@layer components {
- .btn {
- @apply inline-flex items-center justify-center rounded-md text-sm font-medium
- transition-colors focus-visible:outline-none focus-visible:ring-2
- focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50
- disabled:pointer-events-none ring-offset-background;
- }
-
- .btn-primary {
- @apply btn bg-primary text-primary-foreground hover:bg-primary/90 h-10 py-2 px-4;
- }
-
- .btn-secondary {
- @apply btn bg-secondary text-secondary-foreground hover:bg-secondary/80 h-10 py-2 px-4;
- }
-
- .btn-destructive {
- @apply btn bg-destructive text-destructive-foreground hover:bg-destructive/90 h-10 py-2 px-4;
- }
-
- .btn-outline {
- @apply btn border border-input bg-background hover:bg-accent hover:text-accent-foreground h-10 py-2 px-4;
- }
-
- .btn-ghost {
- @apply btn hover:bg-accent hover:text-accent-foreground h-10 py-2 px-4;
- }
-
- .btn-sm {
- @apply h-9 px-3 text-xs;
- }
-
- .btn-lg {
- @apply h-11 px-8;
- }
-
- .input {
- @apply flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm
- ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium
- placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2
- focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed
- disabled:opacity-50;
- }
-
- .card {
- @apply rounded-lg border bg-card text-card-foreground shadow-sm;
- }
-
- .card-header {
- @apply flex flex-col space-y-1.5 p-6;
- }
-
- .card-content {
- @apply p-6 pt-0;
- }
-
- .card-footer {
- @apply flex items-center p-6 pt-0;
- }
-}
-
-/* Custom scrollbar */
-::-webkit-scrollbar {
- width: 8px;
-}
-
-::-webkit-scrollbar-track {
- @apply bg-secondary;
-}
-
-::-webkit-scrollbar-thumb {
- @apply bg-muted-foreground/50 rounded-full;
-}
-
-::-webkit-scrollbar-thumb:hover {
- @apply bg-muted-foreground/70;
-}
-
-/* Animation utilities */
-.animate-in {
- animation: animateIn 0.3s ease-out;
-}
-
-@keyframes animateIn {
- from {
- opacity: 0;
- transform: translateY(10px);
- }
- to {
- opacity: 1;
- transform: translateY(0);
- }
-}
-
-/* Focus ring for accessibility */
-.focus-ring {
- @apply focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:ring-offset-background;
-}
\ No newline at end of file
diff --git a/src/types/index.ts b/src/types/index.ts
deleted file mode 100644
index 3c1986a..0000000
--- a/src/types/index.ts
+++ /dev/null
@@ -1,90 +0,0 @@
-export interface User {
- id: number
- username: string
- role: 'admin' | 'user'
- created_at: string
-}
-
-export interface AuthUser extends User {
- token?: string
-}
-
-export interface LoginCredentials {
- username: string
- password: string
-}
-
-export interface Certificate {
- id: number
- name: string
- type: 'root' | 'intermediate' | 'simple'
- functional_perimeter_id?: number
- perimeter_name?: string
- expiration_date: string
- is_revoked: boolean
- revoked_at?: string
- created_at: string
-}
-
-export interface CreateCertificateData {
- subdomain_name: string
- functional_perimeter_id: number
-}
-
-export interface FunctionalPerimeter {
- id: number
- name: string
- intermediate_cert_name: string
- created_at: string
-}
-
-export interface CreatePerimeterData {
- name: string
- intermediate_passphrase?: string
-}
-
-export interface CreateUserData {
- username: string
- password: string
- role: 'admin' | 'user'
-}
-
-export interface UpdatePasswordData {
- user_id: number
- new_password: string
- confirm_password: string
-}
-
-export interface DashboardStats {
- total_certificates: number
- active_certificates: number
- revoked_certificates: number
- total_perimeters: number
- total_users: number
- expiring_soon: Certificate[]
-}
-
-export interface ApiResponse {
- success: boolean
- data?: T
- message?: string
- errors?: Record
-}
-
-export interface PaginatedResponse {
- data: T[]
- current_page: number
- per_page: number
- total: number
- last_page: number
-}
-
-export type Theme = 'light' | 'dark'
-
-export type Language = 'en' | 'fr' | 'de' | 'es' | 'it' | 'pt' | 'ja' | 'ru' | 'ar' | 'hi' | 'zh'
-
-export interface DownloadCertificateParams {
- type: 'root' | 'intermediate' | 'simple'
- file: string
- perimeter?: string
-}
\ No newline at end of file