diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index c8abc77..045228e 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -335,6 +335,87 @@ const taskService = new TaskService(apiClient); - Services handle all error states - check `result.success` before accessing data - Services return consistent response format: `{ success, status, data?, error? }` +## Using Swagger MCP for Backend API + +This project has access to the **Swagger MCP server** for exploring the backend API definition. The Swagger definition is automatically downloaded and converted from YAML to JSON during the Copilot setup steps. + +### Pre-configured Files + +The setup creates: +- `swagger.json` - The converted Swagger/OpenAPI definition +- `.swagger-mcp` - Configuration file with the path to swagger.json + +### Available Swagger MCP Tools + +#### 1. listEndpoints + +Lists all available API endpoints with their HTTP methods and descriptions: + +``` +swagger-listEndpoints(swaggerFilePath: "/path/to/swagger.json") +``` + +#### 2. listEndpointModels + +Shows the request/response models for a specific endpoint: + +``` +swagger-listEndpointModels( + swaggerFilePath: "/path/to/swagger.json", + path: "/groups/", + method: "GET" +) +``` + +#### 3. generateModelCode + +Generates TypeScript interfaces from Swagger model definitions: + +``` +swagger-generateModelCode( + swaggerFilePath: "/path/to/swagger.json", + modelName: "Group" +) +``` + +#### 4. generateEndpointToolCode + +Generates TypeScript code for calling an API endpoint: + +``` +swagger-generateEndpointToolCode( + swaggerFilePath: "/path/to/swagger.json", + path: "/groups/", + method: "GET" +) +``` + +### When to Use Swagger MCP + +- **Exploring new API endpoints**: Use `listEndpoints` to discover available APIs +- **Understanding request/response formats**: Use `listEndpointModels` and `generateModelCode` +- **Creating new services**: Reference the generated TypeScript interfaces for DTOs +- **Verifying API contracts**: Ensure your service implementations match the backend API + +### Example Workflow + +1. List all endpoints to find the one you need: + ``` + swagger-listEndpoints(swaggerFilePath: "swagger.json") + ``` + +2. Get the models for that endpoint: + ``` + swagger-listEndpointModels(swaggerFilePath: "swagger.json", path: "/groups/", method: "GET") + ``` + +3. Generate TypeScript interface for the model: + ``` + swagger-generateModelCode(swaggerFilePath: "swagger.json", modelName: "Group") + ``` + +4. Use the generated interface to create your DTO in `/src/lib/dto/` + ## Internationalization (i18n) This project uses **Paraglide** for internationalization with support for **English (en)** and **Polish (pl)**. diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml index ebeb561..4b424c3 100644 --- a/.github/workflows/copilot-setup-steps.yml +++ b/.github/workflows/copilot-setup-steps.yml @@ -43,6 +43,20 @@ jobs: npm run build echo "Swagger MCP server built successfully" + - name: Download and convert Swagger definition + run: | + # Download YAML swagger definition from backend docs + curl -s "https://mini-maxit.github.io/backend/master/swagger.yaml" -o /tmp/swagger.yaml + + # Convert YAML to JSON using Python (available on ubuntu-latest) + python3 -c "import yaml, json; print(json.dumps(yaml.safe_load(open('/tmp/swagger.yaml')), indent=2))" > swagger.json + + # Create .swagger-mcp config file pointing to the JSON file + echo "SWAGGER_FILEPATH=$(pwd)/swagger.json" > .swagger-mcp + + echo "Swagger definition downloaded and converted successfully" + echo "Config file created at .swagger-mcp" + - name: Prepare project (sync, typecheck, lint) # Using || true to allow Copilot to start even with minor typecheck/lint issues run: | diff --git a/.gitignore b/.gitignore index 7c6e593..05b8a8f 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,10 @@ vite.config.ts.timestamp-* # Paraglide src/lib/paraglide + +# Logs +logs + +# Swagger MCP +swagger.json +.swagger-mcp diff --git a/messages/en.json b/messages/en.json index e68f2c4..184c15d 100644 --- a/messages/en.json +++ b/messages/en.json @@ -490,5 +490,22 @@ "admin_users_pagination_total": "{total} total users", "admin_users_pagination_prev": "Previous", "admin_users_pagination_next": "Next", - "admin_users_pagination_showing_range": "Showing {from}-{to} of {total} users" + "admin_users_pagination_showing_range": "Showing {from}-{to} of {total} users", + "admin_groups_title": "Group Management", + "admin_groups_search_filter": "Search & Filter", + "admin_groups_search_placeholder": "Search by group name...", + "admin_groups_all_groups": "All Groups", + "admin_groups_showing_count": "Showing {count} of {total} groups", + "admin_groups_load_error": "Failed to load groups", + "admin_groups_no_groups_title": "No groups found", + "admin_groups_no_groups_description": "There are no groups in the system yet.", + "admin_groups_no_matching_title": "No matching groups", + "admin_groups_no_matching_description": "No groups match your search criteria. Try adjusting your filters.", + "admin_groups_column_id": "ID", + "admin_groups_column_name": "Name", + "admin_groups_column_created_by": "Created By", + "admin_groups_column_created_at": "Created", + "admin_groups_column_updated_at": "Updated", + "admin_groups_created_by_prefix": "User #", + "sidebar_admin_groups": "Groups" } diff --git a/messages/pl.json b/messages/pl.json index 00e55df..fd027a8 100644 --- a/messages/pl.json +++ b/messages/pl.json @@ -490,5 +490,22 @@ "admin_users_pagination_total": "{total} użytkowników łącznie", "admin_users_pagination_prev": "Poprzednia", "admin_users_pagination_next": "Następna", - "admin_users_pagination_showing_range": "Wyświetlanie {from}-{to} z {total} użytkowników" + "admin_users_pagination_showing_range": "Wyświetlanie {from}-{to} z {total} użytkowników", + "admin_groups_title": "Zarządzanie Grupami", + "admin_groups_search_filter": "Szukaj i Filtruj", + "admin_groups_search_placeholder": "Szukaj po nazwie grupy...", + "admin_groups_all_groups": "Wszystkie Grupy", + "admin_groups_showing_count": "Wyświetlanie {count} z {total} grup", + "admin_groups_load_error": "Nie udało się załadować grup", + "admin_groups_no_groups_title": "Nie znaleziono grup", + "admin_groups_no_groups_description": "W systemie nie ma jeszcze żadnych grup.", + "admin_groups_no_matching_title": "Brak pasujących grup", + "admin_groups_no_matching_description": "Żadne grupy nie pasują do kryteriów wyszukiwania. Spróbuj dostosować filtry.", + "admin_groups_column_id": "ID", + "admin_groups_column_name": "Nazwa", + "admin_groups_column_created_by": "Utworzony przez", + "admin_groups_column_created_at": "Utworzono", + "admin_groups_column_updated_at": "Zaktualizowano", + "admin_groups_created_by_prefix": "Użytkownik #", + "sidebar_admin_groups": "Grupy" } diff --git a/src/lib/components/dashboard/DashboardSidebar.svelte b/src/lib/components/dashboard/DashboardSidebar.svelte index 92f9a14..44c56bc 100644 --- a/src/lib/components/dashboard/DashboardSidebar.svelte +++ b/src/lib/components/dashboard/DashboardSidebar.svelte @@ -24,6 +24,7 @@ import Globe from '@lucide/svelte/icons/globe'; import UserCircle from '@lucide/svelte/icons/user-circle'; import Users from '@lucide/svelte/icons/users'; + import FolderOpen from '@lucide/svelte/icons/folder-open'; import Languages from '@lucide/svelte/icons/languages'; import LayoutDashboard from '@lucide/svelte/icons/layout-dashboard'; import Activity from '@lucide/svelte/icons/activity'; @@ -90,6 +91,11 @@ title: () => m.sidebar_admin_users(), href: localizeHref(AppRoutes.AdminUsers), icon: Users + }, + { + title: () => m.sidebar_admin_groups(), + href: localizeHref(AppRoutes.AdminGroups), + icon: FolderOpen } ]; diff --git a/src/lib/components/dashboard/admin/groups/GroupsList.svelte b/src/lib/components/dashboard/admin/groups/GroupsList.svelte new file mode 100644 index 0000000..aa77151 --- /dev/null +++ b/src/lib/components/dashboard/admin/groups/GroupsList.svelte @@ -0,0 +1,100 @@ + + +
+ {m.admin_groups_showing_count({ + count: filteredGroups.length, + total: groupsQuery.current.items.length + })} +
+ {/if} +