From 4c6df1f0375e2d8fb1e0014835b07b980d886c05 Mon Sep 17 00:00:00 2001 From: James Wampler Date: Sat, 18 Apr 2026 19:45:16 -0700 Subject: [PATCH] WIP --- CLAUDE.md | 42 +- CLAUDE.original.md | 44 + src/admin/src/api/organizations.ts | 32 + src/admin/src/router/index.ts | 8 +- src/admin/src/types/api.ts | 19 + src/admin/src/utils/validation.ts | 4 + src/admin/src/views/AcceptInviteView.vue | 58 ++ src/admin/src/views/AuditLogsView.vue | 6 - src/admin/src/views/ProfileView.vue | 72 +- src/admin/src/views/SegmentsView.vue | 14 +- src/admin/src/views/SettingsView.vue | 253 ++++- .../OrganizationConfiguration.cs | 2 + ...326_AddOrganizationInviteToken.Designer.cs | 936 ++++++++++++++++++ ...260417015326_AddOrganizationInviteToken.cs | 40 + .../MicCheckDbContextModelSnapshot.cs | 8 + .../Organizations/InviteTokenResponse.cs | 3 + .../InviteUsersByEmailRequest.cs | 7 + .../Organizations/Organization.cs | 1 + .../Organizations/OrganizationService.cs | 65 ++ .../Organizations/OrganizationsController.cs | 48 + 20 files changed, 1537 insertions(+), 125 deletions(-) create mode 100644 CLAUDE.original.md create mode 100644 src/admin/src/utils/validation.ts create mode 100644 src/admin/src/views/AcceptInviteView.vue create mode 100644 src/api/MicCheck.Api/Migrations/20260417015326_AddOrganizationInviteToken.Designer.cs create mode 100644 src/api/MicCheck.Api/Migrations/20260417015326_AddOrganizationInviteToken.cs create mode 100644 src/api/MicCheck.Api/Organizations/InviteTokenResponse.cs create mode 100644 src/api/MicCheck.Api/Organizations/InviteUsersByEmailRequest.cs diff --git a/CLAUDE.md b/CLAUDE.md index 46ccce2..a50fd46 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,44 +1,42 @@ # CLAUDE.md -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. +Guidance for Claude Code (claude.ai/code) when working in this repo. ## Project -MicCheck is an open source project written in asp.net, c#, typescript and VueJS that manages feature flags, their projects, and their environments. +MicCheck: open source. asp.net, c#, typescript, VueJS. Manages feature flags, projects, environments. ## Planned Structure -- `src/admin/` — administrative components in VueJS -- `src/api/` - api and restful endpoints in .NET +- `src/admin/` — VueJS admin components +- `src/api/` - .NET API + REST endpoints - `tests/` — test suite - `docs/` — documentation ## Best Practices -- Ensure all projects are using the latest LTS version of .NET as well as the latest supported nuget packages for that .NET version -- Ensure that langVersion is set to latest in all csproj files and nullable is enabled -- Code in all projects should be organized by feature or area instead of type. (e.g. a features namespace with all feature related code in it or in child namespaces of it) -- All new feature requests should include corresponding unit tests that cover as much of the logic as possible. -- Any file modified should be evaluated for potential test cases and missing coverage areas. +- Use latest LTS .NET + latest supported nuget packages for that version +- Set `langVersion` to latest in all csproj files; enable nullable +- Organize code by feature/area, not type (e.g. `features` namespace) +- New features need unit tests covering as much logic as possible +- Any modified file: evaluate for missing test coverage # Coding -- Use descriptive names for all classes and method created. Avoid generic names like Provider, Manager, Helper -- Coding should match formating and style rules in the .editorconfig file +- Descriptive names for all classes/methods. No generic names: Provider, Manager, Helper +- Match formatting/style from `.editorconfig` ## Testing -- Write unit tests in a BDD style, testing as much code end-to-end as possible without without touching external resources like databases or file systems (e.g. WhenAUserDoesSomething_ThenAThingAppears) -- Mock any external dependencies using Moq. -- Do not name any mocked objects with the word Mock in them -- Do not include any Arrange / Act / Assert comments in the code - +- BDD-style unit tests, end-to-end as possible, no external resources (DB, filesystem). e.g. `WhenAUserDoesSomething_ThenAThingAppears` +- Mock external deps with Moq +- No "Mock" in mocked object names +- No Arrange/Act/Assert comments ## Claude -- Create all plans as .md file located in the `docs/` folder. Admin in `docs/admin/` and API in `docs/api/` -- Divide up large plans into discrete chucks of functionality so each can be built and committed independently. -- When generating a plan from a .md file in /docs/ save the plan in the same folder with the same filename minus the .md extention, but with a _plan.md at the end -- When implementing a plan from a .md file in /docs/ save the summary of the plan in the same folder with the same filename minus the .md extention, but with a _output.md at the end - +- Plans = `.md` files in `docs/`. Admin → `docs/admin/`, API → `docs/api/` +- Split large plans into discrete chunks — each buildable + committable independently +- Plan generated from `docs/.md` → save as `docs/_plan.md` +- Plan implemented from `docs/.md` → save summary as `docs/_output.md` ## Stack -The `.gitignore` is configured for a .NET/Visual Studio project (C#, NuGet, MSBuild). If this changes, update this file accordingly. +`.gitignore` configured for .NET/Visual Studio (C#, NuGet, MSBuild). Update if stack changes. \ No newline at end of file diff --git a/CLAUDE.original.md b/CLAUDE.original.md new file mode 100644 index 0000000..46ccce2 --- /dev/null +++ b/CLAUDE.original.md @@ -0,0 +1,44 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project + +MicCheck is an open source project written in asp.net, c#, typescript and VueJS that manages feature flags, their projects, and their environments. + +## Planned Structure + +- `src/admin/` — administrative components in VueJS +- `src/api/` - api and restful endpoints in .NET +- `tests/` — test suite +- `docs/` — documentation + +## Best Practices + +- Ensure all projects are using the latest LTS version of .NET as well as the latest supported nuget packages for that .NET version +- Ensure that langVersion is set to latest in all csproj files and nullable is enabled +- Code in all projects should be organized by feature or area instead of type. (e.g. a features namespace with all feature related code in it or in child namespaces of it) +- All new feature requests should include corresponding unit tests that cover as much of the logic as possible. +- Any file modified should be evaluated for potential test cases and missing coverage areas. + +# Coding +- Use descriptive names for all classes and method created. Avoid generic names like Provider, Manager, Helper +- Coding should match formating and style rules in the .editorconfig file + +## Testing +- Write unit tests in a BDD style, testing as much code end-to-end as possible without without touching external resources like databases or file systems (e.g. WhenAUserDoesSomething_ThenAThingAppears) +- Mock any external dependencies using Moq. +- Do not name any mocked objects with the word Mock in them +- Do not include any Arrange / Act / Assert comments in the code + + +## Claude +- Create all plans as .md file located in the `docs/` folder. Admin in `docs/admin/` and API in `docs/api/` +- Divide up large plans into discrete chucks of functionality so each can be built and committed independently. +- When generating a plan from a .md file in /docs/ save the plan in the same folder with the same filename minus the .md extention, but with a _plan.md at the end +- When implementing a plan from a .md file in /docs/ save the summary of the plan in the same folder with the same filename minus the .md extention, but with a _output.md at the end + + +## Stack + +The `.gitignore` is configured for a .NET/Visual Studio project (C#, NuGet, MSBuild). If this changes, update this file accordingly. diff --git a/src/admin/src/api/organizations.ts b/src/admin/src/api/organizations.ts index ba77a52..326cc1a 100644 --- a/src/admin/src/api/organizations.ts +++ b/src/admin/src/api/organizations.ts @@ -5,6 +5,9 @@ import type { UpdateOrganizationRequest, OrganizationMemberResponse, InviteUserRequest, + InviteUsersByEmailRequest, + InviteByEmailResult, + InviteTokenResponse, PaginatedResponse, } from '@/types/api'; @@ -49,3 +52,32 @@ export async function inviteOrganizationMember(organizationId: number, request: export async function removeOrganizationMember(organizationId: number, userId: number): Promise { await apiClient.delete(`/v1/organisations/${organizationId}/users/${userId}`); } + +export async function inviteOrganizationMembersByEmail( + organizationId: number, + request: InviteUsersByEmailRequest, +): Promise { + const { data } = await apiClient.post( + `/v1/organisations/${organizationId}/users/invite-by-email`, + request, + ); + return data; +} + +export async function getInviteLink(organizationId: number): Promise { + const { data } = await apiClient.get( + `/v1/organisations/${organizationId}/invite-link`, + ); + return data; +} + +export async function regenerateInviteLink(organizationId: number): Promise { + const { data } = await apiClient.post( + `/v1/organisations/${organizationId}/invite-link/regenerate`, + ); + return data; +} + +export async function acceptInvite(token: string): Promise { + await apiClient.post(`/v1/organisations/invite/${token}/accept`); +} diff --git a/src/admin/src/router/index.ts b/src/admin/src/router/index.ts index 40ffef0..875e563 100644 --- a/src/admin/src/router/index.ts +++ b/src/admin/src/router/index.ts @@ -75,6 +75,12 @@ const routes: RouteRecordRaw[] = [ component: () => import('@/views/LoginView.vue'), meta: { public: true }, }, + { + path: 'accept-invite/:token', + name: 'AcceptInvite', + component: () => import('@/views/AcceptInviteView.vue'), + meta: { public: true, allowAuthenticated: true }, + }, { path: 'register', name: 'Register', @@ -105,7 +111,7 @@ router.beforeEach((to) => { return { name: 'Login', query: { redirect: to.fullPath } }; } - if (isPublic && isAuthenticated) { + if (isPublic && isAuthenticated && !to.matched.some((r) => r.meta.allowAuthenticated)) { return { name: 'Dashboard' }; } diff --git a/src/admin/src/types/api.ts b/src/admin/src/types/api.ts index b2c599c..3caa053 100644 --- a/src/admin/src/types/api.ts +++ b/src/admin/src/types/api.ts @@ -88,6 +88,25 @@ export interface InviteUserRequest { role: 'Admin' | 'User'; } +export interface InviteByEmailEntry { + email: string; + role: 'Admin' | 'User'; +} + +export interface InviteUsersByEmailRequest { + invites: InviteByEmailEntry[]; +} + +export interface InviteByEmailResult { + email: string; + success: boolean; + error: string | null; +} + +export interface InviteTokenResponse { + token: string; +} + // ─── API Keys ───────────────────────────────────────────────────────────────── export interface CreateApiKeyRequest { diff --git a/src/admin/src/utils/validation.ts b/src/admin/src/utils/validation.ts new file mode 100644 index 0000000..6ca9dc1 --- /dev/null +++ b/src/admin/src/utils/validation.ts @@ -0,0 +1,4 @@ +export function validateEmail(v: string): true | string { + if (!v.trim()) return 'Email required'; + return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v.trim()) || 'Invalid email'; +} diff --git a/src/admin/src/views/AcceptInviteView.vue b/src/admin/src/views/AcceptInviteView.vue new file mode 100644 index 0000000..9dfdd6d --- /dev/null +++ b/src/admin/src/views/AcceptInviteView.vue @@ -0,0 +1,58 @@ + + + diff --git a/src/admin/src/views/AuditLogsView.vue b/src/admin/src/views/AuditLogsView.vue index 49ca389..5f3b340 100644 --- a/src/admin/src/views/AuditLogsView.vue +++ b/src/admin/src/views/AuditLogsView.vue @@ -116,11 +116,6 @@ {{ item.resourceType }} - - -