diff --git a/worklenz-frontend/public/locales/alb/all-project-list.json b/worklenz-frontend/public/locales/alb/all-project-list.json index 0e090edb..8079f13d 100644 --- a/worklenz-frontend/public/locales/alb/all-project-list.json +++ b/worklenz-frontend/public/locales/alb/all-project-list.json @@ -3,21 +3,32 @@ "client": "Klienti", "category": "Kategoria", "status": "Statusi", - "tasksProgress": "Progresi i Detyrave", - "updated_at": "Përditësuar Së Fundi", + "tasksProgress": "Përparimi i Detyrave", + "updated_at": "E Përditësuar së Fundi", "members": "Anëtarët", "setting": "Cilësimet", "projects": "Projektet", "refreshProjects": "Rifresko projektet", - "all": "Të Gjitha", - "favorites": "Të Preferuarat", - "archived": "Të Arkivuara", + "all": "Të gjitha", + "favorites": "Të preferuarit", + "archived": "E arkivuar", "placeholder": "Kërko sipas emrit", "archive": "Arkivo", - "unarchive": "Ç'arkivo", - "archiveConfirm": "Jeni i sigurt që doni ta arkivoni këtë projekt?", - "unarchiveConfirm": "Jeni i sigurt që doni ta çarkivoni këtë projekt?", - "clickToFilter": "Klikoni për të filtruar sipas", + "unarchive": "Çarkivo", + "archiveConfirm": "Jeni i sigurt që dëshironi të arkivoni këtë projekt?", + "unarchiveConfirm": "Jeni i sigurt që dëshironi të çarkivoni këtë projekt?", + "yes": "Po", + "no": "Jo", + "clickToFilter": "Kliko për të filtruar sipas", "noProjects": "Nuk u gjetën projekte", - "addToFavourites": "Shto në të preferuarat" + "addToFavourites": "Shto te të preferuarit", + "list": "Lista", + "group": "Grupi", + "listView": "Pamja e Listës", + "groupView": "Pamja e Grupit", + "groupBy": { + "category": "Kategoria", + "client": "Klienti" + }, + "noPermission": "Nuk keni leje për të kryer këtë veprim" } diff --git a/worklenz-frontend/public/locales/de/all-project-list.json b/worklenz-frontend/public/locales/de/all-project-list.json index b11fbbcd..89a9803d 100644 --- a/worklenz-frontend/public/locales/de/all-project-list.json +++ b/worklenz-frontend/public/locales/de/all-project-list.json @@ -17,7 +17,18 @@ "unarchive": "Dearchivieren", "archiveConfirm": "Sind Sie sicher, dass Sie dieses Projekt archivieren möchten?", "unarchiveConfirm": "Sind Sie sicher, dass Sie dieses Projekt dearchivieren möchten?", + "yes": "Ja", + "no": "Nein", "clickToFilter": "Zum Filtern klicken nach", "noProjects": "Keine Projekte gefunden", - "addToFavourites": "Zu Favoriten hinzufügen" + "addToFavourites": "Zu Favoriten hinzufügen", + "list": "Liste", + "group": "Gruppe", + "listView": "Listenansicht", + "groupView": "Gruppenansicht", + "groupBy": { + "category": "Kategorie", + "client": "Kunde" + }, + "noPermission": "Sie haben keine Berechtigung, diese Aktion durchzuführen" } diff --git a/worklenz-frontend/public/locales/en/all-project-list.json b/worklenz-frontend/public/locales/en/all-project-list.json index 86aae0d3..ab98cb6b 100644 --- a/worklenz-frontend/public/locales/en/all-project-list.json +++ b/worklenz-frontend/public/locales/en/all-project-list.json @@ -17,6 +17,8 @@ "unarchive": "Unarchive", "archiveConfirm": "Are you sure you want to archive this project?", "unarchiveConfirm": "Are you sure you want to unarchive this project?", + "yes": "Yes", + "no": "No", "clickToFilter": "Click to filter by", "noProjects": "No projects found", "addToFavourites": "Add to favourites", @@ -27,5 +29,6 @@ "groupBy": { "category": "Category", "client": "Client" - } + }, + "noPermission": "You don't have permission to perform this action" } diff --git a/worklenz-frontend/public/locales/es/all-project-list.json b/worklenz-frontend/public/locales/es/all-project-list.json index 3a0971d1..4a72d9c7 100644 --- a/worklenz-frontend/public/locales/es/all-project-list.json +++ b/worklenz-frontend/public/locales/es/all-project-list.json @@ -3,23 +3,25 @@ "client": "Cliente", "category": "Categoría", "status": "Estado", - "tasksProgress": "Progreso de tareas", - "updated_at": "Última actualización", + "tasksProgress": "Progreso de Tareas", + "updated_at": "Última Actualización", "members": "Miembros", "setting": "Configuración", - "archive": "Archivar", "projects": "Proyectos", "refreshProjects": "Actualizar proyectos", "all": "Todos", "favorites": "Favoritos", "archived": "Archivados", "placeholder": "Buscar por nombre", + "archive": "Archivar", "unarchive": "Desarchivar", - "archiveConfirm": "¿Estás seguro de que deseas archivar este proyecto?", - "unarchiveConfirm": "¿Estás seguro de que deseas desarchivar este proyecto?", - "clickToFilter": "Clique para filtrar por", + "archiveConfirm": "¿Está seguro de que desea archivar este proyecto?", + "unarchiveConfirm": "¿Está seguro de que desea desarchivar este proyecto?", + "yes": "Sí", + "no": "No", + "clickToFilter": "Haga clic para filtrar por", "noProjects": "No se encontraron proyectos", - "addToFavourites": "Añadir a favoritos", + "addToFavourites": "Agregar a favoritos", "list": "Lista", "group": "Grupo", "listView": "Vista de Lista", @@ -27,5 +29,6 @@ "groupBy": { "category": "Categoría", "client": "Cliente" - } + }, + "noPermission": "No tienes permiso para realizar esta acción" } diff --git a/worklenz-frontend/public/locales/pt/all-project-list.json b/worklenz-frontend/public/locales/pt/all-project-list.json index 8d7832fb..482132eb 100644 --- a/worklenz-frontend/public/locales/pt/all-project-list.json +++ b/worklenz-frontend/public/locales/pt/all-project-list.json @@ -6,17 +6,19 @@ "tasksProgress": "Progresso das Tarefas", "updated_at": "Última Atualização", "members": "Membros", - "setting": "Configuração", - "archive": "Arquivar", + "setting": "Configurações", "projects": "Projetos", "refreshProjects": "Atualizar projetos", "all": "Todos", "favorites": "Favoritos", "archived": "Arquivados", "placeholder": "Pesquisar por nome", - "archiveConfirm": "Tem certeza de que deseja arquivar este projeto?", + "archive": "Arquivar", "unarchive": "Desarquivar", + "archiveConfirm": "Tem certeza de que deseja arquivar este projeto?", "unarchiveConfirm": "Tem certeza de que deseja desarquivar este projeto?", + "yes": "Sim", + "no": "Não", "clickToFilter": "Clique para filtrar por", "noProjects": "Nenhum projeto encontrado", "addToFavourites": "Adicionar aos favoritos", @@ -27,5 +29,6 @@ "groupBy": { "category": "Categoria", "client": "Cliente" - } + }, + "noPermission": "Você não tem permissão para realizar esta ação" } diff --git a/worklenz-frontend/public/locales/zh/all-project-list.json b/worklenz-frontend/public/locales/zh/all-project-list.json index 9ff1a707..a6c72c06 100644 --- a/worklenz-frontend/public/locales/zh/all-project-list.json +++ b/worklenz-frontend/public/locales/zh/all-project-list.json @@ -17,7 +17,18 @@ "unarchive": "取消归档", "archiveConfirm": "您确定要归档此项目吗?", "unarchiveConfirm": "您确定要取消归档此项目吗?", - "clickToFilter": "点击以筛选", + "yes": "是", + "no": "否", + "clickToFilter": "点击筛选", "noProjects": "未找到项目", - "addToFavourites": "添加到收藏" + "addToFavourites": "添加到收藏", + "list": "列表", + "group": "分组", + "listView": "列表视图", + "groupView": "分组视图", + "groupBy": { + "category": "类别", + "client": "客户" + }, + "noPermission": "您没有权限执行此操作" } \ No newline at end of file diff --git a/worklenz-frontend/src/app/routes/admin-center-routes.tsx b/worklenz-frontend/src/app/routes/admin-center-routes.tsx index 67ad5b26..6d41ae6c 100644 --- a/worklenz-frontend/src/app/routes/admin-center-routes.tsx +++ b/worklenz-frontend/src/app/routes/admin-center-routes.tsx @@ -1,10 +1,10 @@ import { RouteObject } from 'react-router-dom'; import { Suspense } from 'react'; -import AdminCenterLayout from '@/layouts/admin-center-layout'; import { adminCenterItems } from '@/pages/admin-center/admin-center-constants'; import { Navigate } from 'react-router-dom'; import { useAuthService } from '@/hooks/useAuth'; import { SuspenseFallback } from '@/components/suspense-fallback/suspense-fallback'; +import AdminCenterLayout from '@/layouts/AdminCenterLayout'; const AdminCenterGuard = ({ children }: { children: React.ReactNode }) => { const isOwnerOrAdmin = useAuthService().isOwnerOrAdmin(); diff --git a/worklenz-frontend/src/layouts/admin-center-layout.tsx b/worklenz-frontend/src/layouts/AdminCenterLayout.tsx similarity index 100% rename from worklenz-frontend/src/layouts/admin-center-layout.tsx rename to worklenz-frontend/src/layouts/AdminCenterLayout.tsx diff --git a/worklenz-frontend/src/utils/README-ServiceWorker.md b/worklenz-frontend/src/utils/README-ServiceWorker.md deleted file mode 100644 index 49976316..00000000 --- a/worklenz-frontend/src/utils/README-ServiceWorker.md +++ /dev/null @@ -1,259 +0,0 @@ -# Service Worker Implementation - -This directory contains the service worker implementation for Worklenz, providing offline functionality, caching, and performance improvements. - -## Files Overview - -- **`sw.js`** (in `/public/`) - The main service worker file -- **`serviceWorkerRegistration.ts`** - Registration and management utilities -- **`ServiceWorkerStatus.tsx`** (in `/components/`) - React component for SW status - -## Features - -### 🔄 Caching Strategies - -1. **Cache First** - Static assets (JS, CSS, images) - - Serves from cache first, falls back to network - - Perfect for unchanging resources - -2. **Network First** - API requests - - Tries network first, falls back to cache - - Ensures fresh data when online - -3. **Stale While Revalidate** - HTML pages - - Serves cached version immediately - - Updates cache in background - -### 📱 PWA Features - -- **Offline Support** - App works without internet -- **Installable** - Can be installed on devices -- **Background Sync** - Sync data when online (framework ready) -- **Push Notifications** - Real-time notifications (framework ready) - -## Usage - -### Basic Integration - -The service worker is automatically registered in `App.tsx`: - -```tsx -import { registerSW } from './utils/serviceWorkerRegistration'; - -useEffect(() => { - registerSW({ - onSuccess: (registration) => { - console.log('SW registered successfully'); - }, - onUpdate: (registration) => { - // Show update notification to user - }, - onOfflineReady: () => { - console.log('App ready for offline use'); - } - }); -}, []); -``` - -### Using the Hook - -```tsx -import { useServiceWorker } from '../utils/serviceWorkerRegistration'; - -const MyComponent = () => { - const { isOffline, swManager, clearCache, forceUpdate } = useServiceWorker(); - - return ( -
-

Status: {isOffline ? 'Offline' : 'Online'}

- - -
- ); -}; -``` - -### Status Component - -```tsx -import ServiceWorkerStatus from '../components/service-worker-status/ServiceWorkerStatus'; - -// Minimal offline indicator - - -// Full status with controls - -``` - -## Configuration - -### Cacheable Resources - -Edit the patterns in `sw.js`: - -```javascript -// API endpoints that can be cached -const CACHEABLE_API_PATTERNS = [ - /\/api\/project-categories/, - /\/api\/task-statuses/, - // Add more patterns... -]; - -// Resources that should never be cached -const NEVER_CACHE_PATTERNS = [ - /\/api\/auth\/login/, - /\/socket\.io/, - // Add more patterns... -]; -``` - -### Cache Names - -Update version to force cache refresh: - -```javascript -const CACHE_VERSION = 'v1.0.1'; // Increment when deploying -``` - -## Development - -### Testing Offline - -1. Open DevTools → Application → Service Workers -2. Check "Offline" to simulate offline mode -3. Verify app still functions - -### Debugging - -```javascript -// Check service worker status -navigator.serviceWorker.ready.then(registration => { - console.log('SW ready:', registration); -}); - -// Check cache contents -caches.keys().then(names => { - console.log('Cache names:', names); -}); -``` - -### Cache Management - -```javascript -// Clear all caches -caches.keys().then(names => - Promise.all(names.map(name => caches.delete(name))) -); - -// Clear specific cache -caches.delete('worklenz-api-v1.0.0'); -``` - -## Best Practices - -### 1. Cache Strategy Selection - -- **Static Assets**: Cache First (fast loading) -- **API Data**: Network First (fresh data) -- **User Content**: Network Only (always fresh) -- **App Shell**: Cache First (instant loading) - -### 2. Cache Invalidation - -- Increment `CACHE_VERSION` when deploying -- Use versioned URLs for assets -- Set appropriate cache headers - -### 3. Offline UX - -- Show offline indicators -- Queue actions for later sync -- Provide meaningful offline messages -- Cache critical user data - -### 4. Performance - -- Cache only necessary resources -- Set cache size limits -- Clean up old caches regularly -- Monitor cache usage - -## Monitoring - -### Storage Usage - -```javascript -// Check storage quota -navigator.storage.estimate().then(estimate => { - console.log('Used:', estimate.usage); - console.log('Quota:', estimate.quota); -}); -``` - -### Cache Hit Rate - -Monitor in DevTools → Network: -- Look for "from ServiceWorker" requests -- Check cache effectiveness - -## Troubleshooting - -### Common Issues - -1. **SW not updating** - - Hard refresh (Ctrl+Shift+R) - - Clear browser cache - - Check CACHE_VERSION - -2. **Resources not caching** - - Verify URL patterns - - Check NEVER_CACHE_PATTERNS - - Ensure HTTPS in production - -3. **Offline features not working** - - Verify SW registration - - Check browser support - - Test cache strategies - -### Reset Service Worker - -```javascript -// Unregister and reload -navigator.serviceWorker.getRegistrations().then(registrations => { - registrations.forEach(registration => registration.unregister()); - window.location.reload(); -}); -``` - -## Browser Support - -- ✅ Chrome 45+ -- ✅ Firefox 44+ -- ✅ Safari 11.1+ -- ✅ Edge 17+ -- ❌ Internet Explorer - -## Future Enhancements - -1. **Background Sync** - - Queue offline actions - - Sync when online - -2. **Push Notifications** - - Task assignments - - Project updates - - Deadline reminders - -3. **Advanced Caching** - - Intelligent prefetching - - ML-based cache eviction - - Compression - -4. **Offline Analytics** - - Track offline usage - - Cache hit rates - - Performance metrics - ---- - -*Last updated: January 2025* \ No newline at end of file