@nextsparkjs/core 0.1.0-beta.68 → 0.1.0-beta.69
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/settings/layouts/SettingsSidebar.d.ts.map +1 -1
- package/dist/components/settings/layouts/SettingsSidebar.js +14 -0
- package/dist/components/ui/index.d.ts +7 -0
- package/dist/components/ui/index.d.ts.map +1 -1
- package/dist/components/ui/index.js +7 -0
- package/dist/components/ui/skeleton-dashboard.d.ts +32 -0
- package/dist/components/ui/skeleton-dashboard.d.ts.map +1 -0
- package/dist/components/ui/skeleton-dashboard.js +69 -0
- package/dist/components/ui/skeleton-detail.d.ts.map +1 -1
- package/dist/components/ui/skeleton-detail.js +0 -1
- package/dist/components/ui/skeleton-features.d.ts +27 -0
- package/dist/components/ui/skeleton-features.d.ts.map +1 -0
- package/dist/components/ui/skeleton-features.js +90 -0
- package/dist/components/ui/skeleton-form.d.ts.map +1 -1
- package/dist/components/ui/skeleton-form.js +0 -1
- package/dist/components/ui/skeleton-list.d.ts.map +1 -1
- package/dist/components/ui/skeleton-list.js +0 -1
- package/dist/components/ui/skeleton-public.d.ts +26 -0
- package/dist/components/ui/skeleton-public.d.ts.map +1 -0
- package/dist/components/ui/skeleton-public.js +61 -0
- package/dist/components/ui/skeleton-settings.d.ts +54 -0
- package/dist/components/ui/skeleton-settings.d.ts.map +1 -0
- package/dist/components/ui/skeleton-settings.js +332 -0
- package/dist/components/ui/skeleton.d.ts +23 -1
- package/dist/components/ui/skeleton.d.ts.map +1 -1
- package/dist/components/ui/skeleton.js +46 -2
- package/dist/contexts/TeamContext.d.ts +2 -0
- package/dist/contexts/TeamContext.d.ts.map +1 -1
- package/dist/contexts/TeamContext.js +78 -68
- package/dist/hooks/usePrefetch.d.ts +43 -0
- package/dist/hooks/usePrefetch.d.ts.map +1 -0
- package/dist/hooks/usePrefetch.js +56 -0
- package/dist/lib/actions/index.d.ts +27 -14
- package/dist/lib/actions/index.d.ts.map +1 -1
- package/dist/lib/actions/index.js +19 -1
- package/dist/lib/actions/team.actions.d.ts +107 -0
- package/dist/lib/actions/team.actions.d.ts.map +1 -0
- package/dist/lib/actions/team.actions.js +220 -0
- package/dist/lib/actions/user.actions.d.ts +99 -0
- package/dist/lib/actions/user.actions.d.ts.map +1 -0
- package/dist/lib/actions/user.actions.js +149 -0
- package/dist/messages/de/devtools.json +4 -0
- package/dist/messages/de/index.d.ts +4 -0
- package/dist/messages/de/index.d.ts.map +1 -1
- package/dist/messages/en/devtools.json +16 -0
- package/dist/messages/en/index.d.ts +16 -0
- package/dist/messages/en/index.d.ts.map +1 -1
- package/dist/messages/es/devtools.json +4 -0
- package/dist/messages/es/index.d.ts +4 -0
- package/dist/messages/es/index.d.ts.map +1 -1
- package/dist/messages/fr/devtools.json +4 -0
- package/dist/messages/fr/index.d.ts +4 -0
- package/dist/messages/fr/index.d.ts.map +1 -1
- package/dist/messages/it/devtools.json +4 -0
- package/dist/messages/it/index.d.ts +4 -0
- package/dist/messages/it/index.d.ts.map +1 -1
- package/dist/messages/pt/devtools.json +4 -0
- package/dist/messages/pt/index.d.ts +4 -0
- package/dist/messages/pt/index.d.ts.map +1 -1
- package/dist/nextspark-entities.d.ts +59 -0
- package/dist/styles/classes.json +15 -2
- package/dist/styles/ui.css +1 -1
- package/dist/templates/app/(public)/[...slug]/page.tsx +2 -1
- package/dist/templates/app/(public)/docs/[section]/[page]/page.tsx +1 -1
- package/dist/templates/app/api/devtools/config/entities/route.ts +2 -1
- package/dist/templates/app/api/user/plan-flags/route.ts +1 -1
- package/dist/templates/app/api/v1/[entity]/[id]/child/[childType]/[childId]/route.ts +4 -2
- package/dist/templates/app/dashboard/(main)/layout.tsx +2 -1
- package/dist/templates/app/dashboard/(main)/loading.tsx +5 -0
- package/dist/templates/app/dashboard/features/loading.tsx +5 -0
- package/dist/templates/app/dashboard/settings/api-keys/loading.tsx +5 -0
- package/dist/templates/app/dashboard/settings/billing/loading.tsx +5 -0
- package/dist/templates/app/dashboard/settings/invoices/loading.tsx +5 -0
- package/dist/templates/app/dashboard/settings/loading.tsx +5 -0
- package/dist/templates/app/dashboard/settings/notifications/loading.tsx +5 -0
- package/dist/templates/app/dashboard/settings/password/loading.tsx +5 -0
- package/dist/templates/app/dashboard/settings/plans/loading.tsx +5 -0
- package/dist/templates/app/dashboard/settings/profile/loading.tsx +5 -0
- package/dist/templates/app/dashboard/settings/security/loading.tsx +5 -0
- package/dist/templates/app/dashboard/settings/teams/loading.tsx +5 -0
- package/dist/templates/app/devtools/config/page.tsx +1 -1
- package/dist/templates/app/devtools/page.tsx +1 -1
- package/dist/templates/app/devtools/tests/[[...path]]/page.tsx +1 -1
- package/dist/templates/contents/themes/starter/messages/de/common.json +4 -1
- package/dist/templates/contents/themes/starter/messages/de/index.ts +15 -0
- package/dist/templates/contents/themes/starter/messages/en/common.json +4 -1
- package/dist/templates/contents/themes/starter/messages/en/index.ts +15 -0
- package/dist/templates/contents/themes/starter/messages/es/common.json +4 -1
- package/dist/templates/contents/themes/starter/messages/es/index.ts +15 -0
- package/dist/templates/contents/themes/starter/messages/fr/common.json +4 -1
- package/dist/templates/contents/themes/starter/messages/fr/index.ts +15 -0
- package/dist/templates/contents/themes/starter/messages/it/common.json +4 -1
- package/dist/templates/contents/themes/starter/messages/it/index.ts +13 -0
- package/dist/templates/contents/themes/starter/messages/pt/common.json +4 -1
- package/dist/templates/contents/themes/starter/messages/pt/index.ts +13 -0
- package/dist/templates/contents/themes/starter/styles/globals.css +3 -1
- package/dist/templates/contents/themes/starter/templates/(dashboard)/analytics/loading.tsx +5 -0
- package/dist/templates/contents/themes/starter/templates/(public)/loading.tsx +5 -0
- package/dist/templates/next.config.mjs +5 -0
- package/dist/templates/{middleware.ts → proxy.ts} +5 -6
- package/globals.css +54 -0
- package/nextspark-entities.d.ts +59 -0
- package/package.json +14 -13
- package/scripts/build/registry/discovery/api-presets.mjs +20 -12
- package/scripts/build/registry/generators/api-presets-registry.mjs +18 -5
- package/scripts/build/registry/generators/block-registry.mjs +14 -3
- package/scripts/build/registry/generators/docs-registry.mjs +21 -3
- package/scripts/build/registry/generators/translation-registry.mjs +16 -7
- package/scripts/build/theme.mjs +2 -1
- package/templates/app/(public)/[...slug]/page.tsx +2 -1
- package/templates/app/(public)/docs/[section]/[page]/page.tsx +1 -1
- package/templates/app/api/auth/[...all]/route.ts +13 -5
- package/templates/app/api/devtools/config/entities/route.ts +2 -1
- package/templates/app/api/user/plan-flags/route.ts +1 -1
- package/templates/app/api/v1/[entity]/[id]/child/[childType]/[childId]/route.ts +4 -2
- package/templates/app/dashboard/(main)/layout.tsx +2 -1
- package/templates/app/dashboard/(main)/loading.tsx +5 -0
- package/templates/app/dashboard/features/loading.tsx +5 -0
- package/templates/app/dashboard/settings/api-keys/loading.tsx +5 -0
- package/templates/app/dashboard/settings/billing/loading.tsx +5 -0
- package/templates/app/dashboard/settings/invoices/loading.tsx +5 -0
- package/templates/app/dashboard/settings/loading.tsx +5 -0
- package/templates/app/dashboard/settings/notifications/loading.tsx +5 -0
- package/templates/app/dashboard/settings/password/loading.tsx +5 -0
- package/templates/app/dashboard/settings/plans/loading.tsx +5 -0
- package/templates/app/dashboard/settings/profile/loading.tsx +5 -0
- package/templates/app/dashboard/settings/security/loading.tsx +5 -0
- package/templates/app/dashboard/settings/teams/loading.tsx +5 -0
- package/templates/app/devtools/config/page.tsx +1 -1
- package/templates/app/devtools/page.tsx +1 -1
- package/templates/app/devtools/tests/[[...path]]/page.tsx +1 -1
- package/templates/contents/themes/starter/config/app.config.ts +8 -7
- package/templates/contents/themes/starter/messages/de/common.json +4 -1
- package/templates/contents/themes/starter/messages/de/index.ts +15 -0
- package/templates/contents/themes/starter/messages/en/common.json +4 -1
- package/templates/contents/themes/starter/messages/en/index.ts +15 -0
- package/templates/contents/themes/starter/messages/es/common.json +4 -1
- package/templates/contents/themes/starter/messages/es/index.ts +15 -0
- package/templates/contents/themes/starter/messages/fr/common.json +4 -1
- package/templates/contents/themes/starter/messages/fr/index.ts +15 -0
- package/templates/contents/themes/starter/messages/it/common.json +4 -1
- package/templates/contents/themes/starter/messages/it/index.ts +13 -0
- package/templates/contents/themes/starter/messages/pt/common.json +4 -1
- package/templates/contents/themes/starter/messages/pt/index.ts +13 -0
- package/templates/contents/themes/starter/styles/globals.css +3 -1
- package/templates/contents/themes/starter/templates/(dashboard)/analytics/loading.tsx +5 -0
- package/templates/contents/themes/starter/templates/(public)/loading.tsx +5 -0
- package/templates/next.config.mjs +5 -0
- package/templates/pnpm-workspace.yaml +5 -0
- package/templates/{middleware.ts → proxy.ts} +5 -6
- package/tests/jest/setup.ts +5 -0
- package/dist/presets/plugin/.env.example.template +0 -19
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usePrefetch.d.ts","sourceRoot":"","sources":["../../src/hooks/usePrefetch.ts"],"names":[],"mappings":"AAyBA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,mBAAmB;;;EAuBlC;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,cAgBnD"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useQueryClient } from "@tanstack/react-query";
|
|
3
|
+
import { useCallback } from "react";
|
|
4
|
+
import { TEAMS_QUERY_KEY, fetchUserTeams } from "../contexts/TeamContext.js";
|
|
5
|
+
const USER_PROFILE_QUERY_KEY = ["user-profile"];
|
|
6
|
+
async function fetchUserProfile() {
|
|
7
|
+
const response = await fetch("/api/user/profile");
|
|
8
|
+
if (!response.ok) {
|
|
9
|
+
throw new Error("Failed to fetch profile");
|
|
10
|
+
}
|
|
11
|
+
return response.json();
|
|
12
|
+
}
|
|
13
|
+
function usePrefetchSettings() {
|
|
14
|
+
const queryClient = useQueryClient();
|
|
15
|
+
const prefetchProfile = useCallback(() => {
|
|
16
|
+
queryClient.prefetchQuery({
|
|
17
|
+
queryKey: USER_PROFILE_QUERY_KEY,
|
|
18
|
+
queryFn: fetchUserProfile,
|
|
19
|
+
staleTime: 1e3 * 60 * 5
|
|
20
|
+
// 5 minutes
|
|
21
|
+
});
|
|
22
|
+
}, [queryClient]);
|
|
23
|
+
const prefetchTeams = useCallback(() => {
|
|
24
|
+
queryClient.prefetchQuery({
|
|
25
|
+
queryKey: TEAMS_QUERY_KEY,
|
|
26
|
+
queryFn: fetchUserTeams,
|
|
27
|
+
staleTime: 1e3 * 60 * 5
|
|
28
|
+
// 5 minutes
|
|
29
|
+
});
|
|
30
|
+
}, [queryClient]);
|
|
31
|
+
return {
|
|
32
|
+
prefetchProfile,
|
|
33
|
+
prefetchTeams
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function usePrefetchEntity(entitySlug) {
|
|
37
|
+
const queryClient = useQueryClient();
|
|
38
|
+
return useCallback(() => {
|
|
39
|
+
queryClient.prefetchQuery({
|
|
40
|
+
queryKey: ["entity", entitySlug, "list"],
|
|
41
|
+
queryFn: async () => {
|
|
42
|
+
const response = await fetch(`/api/v1/${entitySlug}?limit=20`);
|
|
43
|
+
if (!response.ok) {
|
|
44
|
+
throw new Error(`Failed to fetch ${entitySlug}`);
|
|
45
|
+
}
|
|
46
|
+
return response.json();
|
|
47
|
+
},
|
|
48
|
+
staleTime: 1e3 * 60 * 2
|
|
49
|
+
// 2 minutes for entity lists
|
|
50
|
+
});
|
|
51
|
+
}, [queryClient, entitySlug]);
|
|
52
|
+
}
|
|
53
|
+
export {
|
|
54
|
+
usePrefetchEntity,
|
|
55
|
+
usePrefetchSettings
|
|
56
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* NextSpark Server Actions
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Server Actions for CRUD operations, user management, and team management.
|
|
5
5
|
* Auth (userId/teamId) is obtained automatically from session and cookies.
|
|
6
6
|
*
|
|
7
7
|
* Import from '@nextsparkjs/core/actions' or '@nextsparkjs/core/lib/actions'.
|
|
@@ -9,31 +9,44 @@
|
|
|
9
9
|
* @example
|
|
10
10
|
* ```typescript
|
|
11
11
|
* import {
|
|
12
|
+
* // Entity actions
|
|
12
13
|
* createEntity,
|
|
13
14
|
* updateEntity,
|
|
14
15
|
* deleteEntity,
|
|
15
16
|
* getEntity,
|
|
16
17
|
* listEntities,
|
|
18
|
+
* // User actions
|
|
19
|
+
* updateProfile,
|
|
20
|
+
* updateAvatar,
|
|
21
|
+
* deleteAccount,
|
|
22
|
+
* // Team actions
|
|
23
|
+
* updateTeam,
|
|
24
|
+
* inviteMember,
|
|
25
|
+
* removeMember,
|
|
26
|
+
* updateMemberRole,
|
|
17
27
|
* } from '@nextsparkjs/core/actions'
|
|
18
28
|
*
|
|
19
|
-
* //
|
|
29
|
+
* // Entity operations - auth obtained from server session
|
|
20
30
|
* const result = await createEntity('schools', { name: 'MIT' })
|
|
21
|
-
*
|
|
22
|
-
* // Update with revalidation
|
|
23
|
-
* await updateEntity('campaigns', id, { status: 'paused' }, {
|
|
24
|
-
* revalidatePaths: ['/dashboard']
|
|
25
|
-
* })
|
|
26
|
-
*
|
|
27
|
-
* // Delete with redirect
|
|
31
|
+
* await updateEntity('campaigns', id, { status: 'paused' })
|
|
28
32
|
* await deleteEntity('leads', id, { redirectTo: '/leads' })
|
|
29
33
|
*
|
|
30
|
-
* //
|
|
31
|
-
*
|
|
34
|
+
* // User profile operations
|
|
35
|
+
* await updateProfile({ firstName: 'John', lastName: 'Doe' })
|
|
36
|
+
* await updateAvatar(formData)
|
|
37
|
+
* await deleteAccount()
|
|
32
38
|
*
|
|
33
|
-
* //
|
|
34
|
-
*
|
|
39
|
+
* // Team operations
|
|
40
|
+
* await updateTeam('team-id', { name: 'New Name' })
|
|
41
|
+
* await inviteMember('team-id', 'user@example.com', 'member')
|
|
42
|
+
* await removeMember('team-id', 'member-id')
|
|
43
|
+
* await updateMemberRole('team-id', 'member-id', 'admin')
|
|
35
44
|
* ```
|
|
36
45
|
*/
|
|
37
46
|
export { createEntity, updateEntity, deleteEntity, getEntity, listEntities, deleteEntities, entityExists, countEntities, } from './entity.actions';
|
|
47
|
+
export { updateProfile, updateAvatar, deleteAccount, } from './user.actions';
|
|
48
|
+
export type { UpdateProfileData, ProfileUpdateResult, } from './user.actions';
|
|
49
|
+
export { updateTeam, inviteMember, removeMember, updateMemberRole, } from './team.actions';
|
|
50
|
+
export type { UpdateTeamData, InviteMemberResult, } from './team.actions';
|
|
38
51
|
export type { EntityActionResult, EntityActionVoidResult, CreateEntityInput, UpdateEntityInput, ListEntityOptions, ListEntityResult, ActionConfig, ActionAuthContext, BatchDeleteResult, } from './types';
|
|
39
52
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/actions/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/actions/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AAMH,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,SAAS,EACT,YAAY,EACZ,cAAc,EACd,YAAY,EACZ,aAAa,GACd,MAAM,kBAAkB,CAAA;AAMzB,OAAO,EACL,aAAa,EACb,YAAY,EACZ,aAAa,GACd,MAAM,gBAAgB,CAAA;AAEvB,YAAY,EACV,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,gBAAgB,CAAA;AAMvB,OAAO,EACL,UAAU,EACV,YAAY,EACZ,YAAY,EACZ,gBAAgB,GACjB,MAAM,gBAAgB,CAAA;AAEvB,YAAY,EACV,cAAc,EACd,kBAAkB,GACnB,MAAM,gBAAgB,CAAA;AAMvB,YAAY,EACV,kBAAkB,EAClB,sBAAsB,EACtB,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EAChB,YAAY,EACZ,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,SAAS,CAAA"}
|
|
@@ -8,13 +8,31 @@ import {
|
|
|
8
8
|
entityExists,
|
|
9
9
|
countEntities
|
|
10
10
|
} from "./entity.actions.js";
|
|
11
|
+
import {
|
|
12
|
+
updateProfile,
|
|
13
|
+
updateAvatar,
|
|
14
|
+
deleteAccount
|
|
15
|
+
} from "./user.actions.js";
|
|
16
|
+
import {
|
|
17
|
+
updateTeam,
|
|
18
|
+
inviteMember,
|
|
19
|
+
removeMember,
|
|
20
|
+
updateMemberRole
|
|
21
|
+
} from "./team.actions.js";
|
|
11
22
|
export {
|
|
12
23
|
countEntities,
|
|
13
24
|
createEntity,
|
|
25
|
+
deleteAccount,
|
|
14
26
|
deleteEntities,
|
|
15
27
|
deleteEntity,
|
|
16
28
|
entityExists,
|
|
17
29
|
getEntity,
|
|
30
|
+
inviteMember,
|
|
18
31
|
listEntities,
|
|
19
|
-
|
|
32
|
+
removeMember,
|
|
33
|
+
updateAvatar,
|
|
34
|
+
updateEntity,
|
|
35
|
+
updateMemberRole,
|
|
36
|
+
updateProfile,
|
|
37
|
+
updateTeam
|
|
20
38
|
};
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import type { Team, TeamRole, TeamMember } from '../teams/types';
|
|
2
|
+
import type { EntityActionResult, EntityActionVoidResult } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Team update data that can be passed to updateTeam
|
|
5
|
+
*/
|
|
6
|
+
export interface UpdateTeamData {
|
|
7
|
+
name?: string;
|
|
8
|
+
slug?: string;
|
|
9
|
+
description?: string;
|
|
10
|
+
avatarUrl?: string;
|
|
11
|
+
settings?: Record<string, unknown>;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Result of invite member action
|
|
15
|
+
*/
|
|
16
|
+
export interface InviteMemberResult {
|
|
17
|
+
memberId: string;
|
|
18
|
+
userId: string;
|
|
19
|
+
teamId: string;
|
|
20
|
+
role: TeamRole;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Update a team's information
|
|
24
|
+
*
|
|
25
|
+
* Requires owner or admin role in the team.
|
|
26
|
+
*
|
|
27
|
+
* @param teamId - The team ID to update
|
|
28
|
+
* @param data - Team fields to update
|
|
29
|
+
* @returns Updated team data wrapped in EntityActionResult
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```typescript
|
|
33
|
+
* const result = await updateTeam('team-123', {
|
|
34
|
+
* name: 'New Team Name',
|
|
35
|
+
* description: 'Updated description'
|
|
36
|
+
* })
|
|
37
|
+
*
|
|
38
|
+
* if (result.success) {
|
|
39
|
+
* console.log('Team updated:', result.data.name)
|
|
40
|
+
* }
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
export declare function updateTeam(teamId: string, data: UpdateTeamData): Promise<EntityActionResult<Team>>;
|
|
44
|
+
/**
|
|
45
|
+
* Invite a user to join the team
|
|
46
|
+
*
|
|
47
|
+
* Requires owner or admin role in the team.
|
|
48
|
+
* The user must already exist in the system.
|
|
49
|
+
*
|
|
50
|
+
* @param teamId - The team ID
|
|
51
|
+
* @param email - Email of the user to invite
|
|
52
|
+
* @param role - Role to assign (member, admin, viewer)
|
|
53
|
+
* @returns Created membership data wrapped in EntityActionResult
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```typescript
|
|
57
|
+
* const result = await inviteMember('team-123', 'user@example.com', 'member')
|
|
58
|
+
*
|
|
59
|
+
* if (result.success) {
|
|
60
|
+
* console.log('Member invited:', result.data.memberId)
|
|
61
|
+
* }
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
export declare function inviteMember(teamId: string, email: string, role?: TeamRole): Promise<EntityActionResult<InviteMemberResult>>;
|
|
65
|
+
/**
|
|
66
|
+
* Remove a member from the team
|
|
67
|
+
*
|
|
68
|
+
* Requires owner or admin role in the team.
|
|
69
|
+
* Cannot remove the team owner (must transfer ownership first).
|
|
70
|
+
*
|
|
71
|
+
* @param teamId - The team ID
|
|
72
|
+
* @param memberId - The member ID to remove (from team_members table)
|
|
73
|
+
* @returns Success status
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* ```typescript
|
|
77
|
+
* const result = await removeMember('team-123', 'member-id-456')
|
|
78
|
+
*
|
|
79
|
+
* if (result.success) {
|
|
80
|
+
* console.log('Member removed')
|
|
81
|
+
* }
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
export declare function removeMember(teamId: string, memberId: string): Promise<EntityActionVoidResult>;
|
|
85
|
+
/**
|
|
86
|
+
* Update a team member's role
|
|
87
|
+
*
|
|
88
|
+
* Requires owner role to change to/from admin.
|
|
89
|
+
* Admins can change member/viewer roles.
|
|
90
|
+
* Cannot change owner role (must transfer ownership).
|
|
91
|
+
*
|
|
92
|
+
* @param teamId - The team ID
|
|
93
|
+
* @param memberId - The member's user ID
|
|
94
|
+
* @param role - New role to assign
|
|
95
|
+
* @returns Updated membership data wrapped in EntityActionResult
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* ```typescript
|
|
99
|
+
* const result = await updateMemberRole('team-123', 'user-456', 'admin')
|
|
100
|
+
*
|
|
101
|
+
* if (result.success) {
|
|
102
|
+
* console.log('Role updated:', result.data.role)
|
|
103
|
+
* }
|
|
104
|
+
* ```
|
|
105
|
+
*/
|
|
106
|
+
export declare function updateMemberRole(teamId: string, memberId: string, role: TeamRole): Promise<EntityActionResult<TeamMember>>;
|
|
107
|
+
//# sourceMappingURL=team.actions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"team.actions.d.ts","sourceRoot":"","sources":["../../../src/lib/actions/team.actions.ts"],"names":[],"mappings":"AA4CA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAChE,OAAO,KAAK,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAA;AAMzE;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACnC;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,QAAQ,CAAA;CACf;AAgDD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,UAAU,CAC9B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,cAAc,GACnB,OAAO,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC,CA4DnC;AAMD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,IAAI,GAAE,QAAmB,GACxB,OAAO,CAAC,kBAAkB,CAAC,kBAAkB,CAAC,CAAC,CAqEjD;AAMD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,sBAAsB,CAAC,CAgEjC;AAMD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,QAAQ,GACb,OAAO,CAAC,kBAAkB,CAAC,UAAU,CAAC,CAAC,CAsEzC"}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
"use server";
|
|
2
|
+
import { revalidatePath } from "next/cache";
|
|
3
|
+
import { headers } from "next/headers";
|
|
4
|
+
import { getTypedSession } from "../auth.js";
|
|
5
|
+
import { TeamService } from "../services/team.service.js";
|
|
6
|
+
import { TeamMemberService } from "../services/team-member.service.js";
|
|
7
|
+
async function getAuthUser() {
|
|
8
|
+
var _a;
|
|
9
|
+
const headersList = await headers();
|
|
10
|
+
const session = await getTypedSession(headersList);
|
|
11
|
+
if (!((_a = session == null ? void 0 : session.user) == null ? void 0 : _a.id)) {
|
|
12
|
+
return { success: false, error: "Authentication required" };
|
|
13
|
+
}
|
|
14
|
+
return { success: true, userId: session.user.id };
|
|
15
|
+
}
|
|
16
|
+
async function verifyTeamPermission(userId, teamId, requiredRoles = ["owner", "admin"]) {
|
|
17
|
+
const hasPermission = await TeamMemberService.hasPermission(
|
|
18
|
+
userId,
|
|
19
|
+
teamId,
|
|
20
|
+
requiredRoles
|
|
21
|
+
);
|
|
22
|
+
if (!hasPermission) {
|
|
23
|
+
return { success: false, error: "Permission denied. Required role: " + requiredRoles.join(" or ") };
|
|
24
|
+
}
|
|
25
|
+
return { success: true };
|
|
26
|
+
}
|
|
27
|
+
async function updateTeam(teamId, data) {
|
|
28
|
+
try {
|
|
29
|
+
if (!(teamId == null ? void 0 : teamId.trim())) {
|
|
30
|
+
return { success: false, error: "Team ID is required" };
|
|
31
|
+
}
|
|
32
|
+
if (!data || Object.keys(data).length === 0) {
|
|
33
|
+
return { success: false, error: "No fields provided for update" };
|
|
34
|
+
}
|
|
35
|
+
const authResult = await getAuthUser();
|
|
36
|
+
if (!authResult.success) {
|
|
37
|
+
return { success: false, error: authResult.error };
|
|
38
|
+
}
|
|
39
|
+
const { userId } = authResult;
|
|
40
|
+
const permResult = await verifyTeamPermission(userId, teamId, ["owner", "admin"]);
|
|
41
|
+
if (!permResult.success) {
|
|
42
|
+
return { success: false, error: permResult.error };
|
|
43
|
+
}
|
|
44
|
+
const updatePayload = {};
|
|
45
|
+
if (data.name !== void 0) updatePayload.name = data.name;
|
|
46
|
+
if (data.slug !== void 0) updatePayload.slug = data.slug;
|
|
47
|
+
if (data.description !== void 0) updatePayload.description = data.description;
|
|
48
|
+
if (data.avatarUrl !== void 0) updatePayload.avatarUrl = data.avatarUrl;
|
|
49
|
+
if (data.settings !== void 0) updatePayload.settings = data.settings;
|
|
50
|
+
if (Object.keys(updatePayload).length === 0) {
|
|
51
|
+
return { success: false, error: "No valid fields provided for update" };
|
|
52
|
+
}
|
|
53
|
+
if (updatePayload.slug) {
|
|
54
|
+
const isAvailable = await TeamService.isSlugAvailable(updatePayload.slug, teamId);
|
|
55
|
+
if (!isAvailable) {
|
|
56
|
+
return { success: false, error: "Team slug is already taken" };
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
const updatedTeam = await TeamService.update(teamId, updatePayload, userId);
|
|
60
|
+
revalidatePath("/dashboard/settings/team");
|
|
61
|
+
revalidatePath("/dashboard");
|
|
62
|
+
return { success: true, data: updatedTeam };
|
|
63
|
+
} catch (error) {
|
|
64
|
+
console.error("[updateTeam] Error:", error);
|
|
65
|
+
return {
|
|
66
|
+
success: false,
|
|
67
|
+
error: error instanceof Error ? error.message : "Failed to update team"
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
async function inviteMember(teamId, email, role = "member") {
|
|
72
|
+
try {
|
|
73
|
+
if (!(teamId == null ? void 0 : teamId.trim())) {
|
|
74
|
+
return { success: false, error: "Team ID is required" };
|
|
75
|
+
}
|
|
76
|
+
if (!(email == null ? void 0 : email.trim())) {
|
|
77
|
+
return { success: false, error: "Email is required" };
|
|
78
|
+
}
|
|
79
|
+
const validRoles = ["admin", "member", "viewer"];
|
|
80
|
+
if (!validRoles.includes(role)) {
|
|
81
|
+
return { success: false, error: "Invalid role. Must be admin, member, or viewer" };
|
|
82
|
+
}
|
|
83
|
+
const authResult = await getAuthUser();
|
|
84
|
+
if (!authResult.success) {
|
|
85
|
+
return { success: false, error: authResult.error };
|
|
86
|
+
}
|
|
87
|
+
const { userId } = authResult;
|
|
88
|
+
const permResult = await verifyTeamPermission(userId, teamId, ["owner", "admin"]);
|
|
89
|
+
if (!permResult.success) {
|
|
90
|
+
return { success: false, error: permResult.error };
|
|
91
|
+
}
|
|
92
|
+
const { UserService } = await import("../services/user.service.js");
|
|
93
|
+
const targetUser = await UserService.getUserByEmail(email, userId);
|
|
94
|
+
if (!targetUser) {
|
|
95
|
+
return { success: false, error: "User not found. They must create an account first." };
|
|
96
|
+
}
|
|
97
|
+
const existingMember = await TeamMemberService.getByTeamAndUser(teamId, targetUser.id);
|
|
98
|
+
if (existingMember) {
|
|
99
|
+
return { success: false, error: "User is already a member of this team" };
|
|
100
|
+
}
|
|
101
|
+
const member = await TeamMemberService.add(teamId, targetUser.id, role, {
|
|
102
|
+
invitedBy: userId
|
|
103
|
+
});
|
|
104
|
+
revalidatePath("/dashboard/settings/team");
|
|
105
|
+
revalidatePath("/dashboard/settings/team/members");
|
|
106
|
+
return {
|
|
107
|
+
success: true,
|
|
108
|
+
data: {
|
|
109
|
+
memberId: member.id,
|
|
110
|
+
userId: member.userId,
|
|
111
|
+
teamId: member.teamId,
|
|
112
|
+
role: member.role
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
} catch (error) {
|
|
116
|
+
console.error("[inviteMember] Error:", error);
|
|
117
|
+
return {
|
|
118
|
+
success: false,
|
|
119
|
+
error: error instanceof Error ? error.message : "Failed to invite member"
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
async function removeMember(teamId, memberId) {
|
|
124
|
+
try {
|
|
125
|
+
if (!(teamId == null ? void 0 : teamId.trim())) {
|
|
126
|
+
return { success: false, error: "Team ID is required" };
|
|
127
|
+
}
|
|
128
|
+
if (!(memberId == null ? void 0 : memberId.trim())) {
|
|
129
|
+
return { success: false, error: "Member ID is required" };
|
|
130
|
+
}
|
|
131
|
+
const authResult = await getAuthUser();
|
|
132
|
+
if (!authResult.success) {
|
|
133
|
+
return { success: false, error: authResult.error };
|
|
134
|
+
}
|
|
135
|
+
const { userId } = authResult;
|
|
136
|
+
const permResult = await verifyTeamPermission(userId, teamId, ["owner", "admin"]);
|
|
137
|
+
if (!permResult.success) {
|
|
138
|
+
return { success: false, error: permResult.error };
|
|
139
|
+
}
|
|
140
|
+
const targetUserId = memberId;
|
|
141
|
+
if (targetUserId === userId) {
|
|
142
|
+
const selfRole = await TeamMemberService.getRole(teamId, userId);
|
|
143
|
+
if (selfRole === "owner") {
|
|
144
|
+
return { success: false, error: "Cannot remove yourself as owner. Transfer ownership first." };
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
const targetRole = await TeamMemberService.getRole(teamId, targetUserId);
|
|
148
|
+
if (targetRole === "owner") {
|
|
149
|
+
return { success: false, error: "Cannot remove team owner. Transfer ownership first." };
|
|
150
|
+
}
|
|
151
|
+
const requestorRole = await TeamMemberService.getRole(teamId, userId);
|
|
152
|
+
if (requestorRole === "admin" && targetRole === "admin" && targetUserId !== userId) {
|
|
153
|
+
return { success: false, error: "Admins cannot remove other admins. Only the owner can do that." };
|
|
154
|
+
}
|
|
155
|
+
await TeamMemberService.remove(teamId, targetUserId);
|
|
156
|
+
revalidatePath("/dashboard/settings/team");
|
|
157
|
+
revalidatePath("/dashboard/settings/team/members");
|
|
158
|
+
return { success: true };
|
|
159
|
+
} catch (error) {
|
|
160
|
+
console.error("[removeMember] Error:", error);
|
|
161
|
+
return {
|
|
162
|
+
success: false,
|
|
163
|
+
error: error instanceof Error ? error.message : "Failed to remove member"
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
async function updateMemberRole(teamId, memberId, role) {
|
|
168
|
+
try {
|
|
169
|
+
if (!(teamId == null ? void 0 : teamId.trim())) {
|
|
170
|
+
return { success: false, error: "Team ID is required" };
|
|
171
|
+
}
|
|
172
|
+
if (!(memberId == null ? void 0 : memberId.trim())) {
|
|
173
|
+
return { success: false, error: "Member ID is required" };
|
|
174
|
+
}
|
|
175
|
+
const validRoles = ["admin", "member", "viewer"];
|
|
176
|
+
if (!validRoles.includes(role)) {
|
|
177
|
+
return { success: false, error: "Invalid role. Must be admin, member, or viewer. Use transferOwnership for owner." };
|
|
178
|
+
}
|
|
179
|
+
const authResult = await getAuthUser();
|
|
180
|
+
if (!authResult.success) {
|
|
181
|
+
return { success: false, error: authResult.error };
|
|
182
|
+
}
|
|
183
|
+
const { userId } = authResult;
|
|
184
|
+
const requestorRole = await TeamMemberService.getRole(teamId, userId);
|
|
185
|
+
if (!requestorRole) {
|
|
186
|
+
return { success: false, error: "You are not a member of this team" };
|
|
187
|
+
}
|
|
188
|
+
const targetUserId = memberId;
|
|
189
|
+
const currentRole = await TeamMemberService.getRole(teamId, targetUserId);
|
|
190
|
+
if (!currentRole) {
|
|
191
|
+
return { success: false, error: "Member not found in this team" };
|
|
192
|
+
}
|
|
193
|
+
if (currentRole === "owner") {
|
|
194
|
+
return { success: false, error: "Cannot change owner role. Use transferOwnership instead." };
|
|
195
|
+
}
|
|
196
|
+
if ((role === "admin" || currentRole === "admin") && requestorRole !== "owner") {
|
|
197
|
+
return { success: false, error: "Only the team owner can promote to or demote from admin" };
|
|
198
|
+
}
|
|
199
|
+
if (requestorRole === "admin" && (role === "member" || role === "viewer")) {
|
|
200
|
+
} else if (requestorRole !== "owner" && requestorRole !== "admin") {
|
|
201
|
+
return { success: false, error: "Permission denied. You must be an owner or admin." };
|
|
202
|
+
}
|
|
203
|
+
const updatedMember = await TeamMemberService.updateRole(teamId, targetUserId, role);
|
|
204
|
+
revalidatePath("/dashboard/settings/team");
|
|
205
|
+
revalidatePath("/dashboard/settings/team/members");
|
|
206
|
+
return { success: true, data: updatedMember };
|
|
207
|
+
} catch (error) {
|
|
208
|
+
console.error("[updateMemberRole] Error:", error);
|
|
209
|
+
return {
|
|
210
|
+
success: false,
|
|
211
|
+
error: error instanceof Error ? error.message : "Failed to update member role"
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
export {
|
|
216
|
+
inviteMember,
|
|
217
|
+
removeMember,
|
|
218
|
+
updateMemberRole,
|
|
219
|
+
updateTeam
|
|
220
|
+
};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { EntityActionResult, EntityActionVoidResult } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Profile update data that can be passed to updateProfile
|
|
4
|
+
*/
|
|
5
|
+
export interface UpdateProfileData {
|
|
6
|
+
firstName?: string;
|
|
7
|
+
lastName?: string;
|
|
8
|
+
name?: string;
|
|
9
|
+
country?: string;
|
|
10
|
+
timezone?: string;
|
|
11
|
+
language?: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Result of profile update with user data
|
|
15
|
+
*/
|
|
16
|
+
export interface ProfileUpdateResult {
|
|
17
|
+
id: string;
|
|
18
|
+
email: string;
|
|
19
|
+
name?: string;
|
|
20
|
+
firstName?: string;
|
|
21
|
+
lastName?: string;
|
|
22
|
+
image?: string;
|
|
23
|
+
country?: string;
|
|
24
|
+
timezone?: string;
|
|
25
|
+
language?: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Update the current user's profile information
|
|
29
|
+
*
|
|
30
|
+
* Auth is automatically obtained from session - users can only update their own profile.
|
|
31
|
+
*
|
|
32
|
+
* @param data - Profile fields to update
|
|
33
|
+
* @returns Updated user data wrapped in EntityActionResult
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```typescript
|
|
37
|
+
* const result = await updateProfile({
|
|
38
|
+
* firstName: 'John',
|
|
39
|
+
* lastName: 'Doe',
|
|
40
|
+
* timezone: 'America/New_York'
|
|
41
|
+
* })
|
|
42
|
+
*
|
|
43
|
+
* if (result.success) {
|
|
44
|
+
* console.log('Profile updated:', result.data)
|
|
45
|
+
* } else {
|
|
46
|
+
* console.error('Error:', result.error)
|
|
47
|
+
* }
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
export declare function updateProfile(data: UpdateProfileData): Promise<EntityActionResult<ProfileUpdateResult>>;
|
|
51
|
+
/**
|
|
52
|
+
* Update the current user's avatar image
|
|
53
|
+
*
|
|
54
|
+
* Accepts FormData with an 'avatar' or 'image' field containing the image URL or file path.
|
|
55
|
+
* For actual file uploads, the file should be uploaded to storage first and the URL passed here.
|
|
56
|
+
*
|
|
57
|
+
* @param formData - FormData with 'avatar' or 'image' field containing the image URL
|
|
58
|
+
* @returns Updated user data wrapped in EntityActionResult
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```typescript
|
|
62
|
+
* // After uploading file to storage
|
|
63
|
+
* const formData = new FormData()
|
|
64
|
+
* formData.append('avatar', imageUrl)
|
|
65
|
+
* const result = await updateAvatar(formData)
|
|
66
|
+
*
|
|
67
|
+
* if (result.success) {
|
|
68
|
+
* console.log('Avatar updated:', result.data.image)
|
|
69
|
+
* }
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
export declare function updateAvatar(formData: FormData): Promise<EntityActionResult<ProfileUpdateResult>>;
|
|
73
|
+
/**
|
|
74
|
+
* Delete the current user's account
|
|
75
|
+
*
|
|
76
|
+
* This is a destructive operation that:
|
|
77
|
+
* - Removes the user from all teams
|
|
78
|
+
* - Deletes user metadata
|
|
79
|
+
* - Deletes the user account
|
|
80
|
+
*
|
|
81
|
+
* Teams owned by the user must have ownership transferred first or be deleted.
|
|
82
|
+
*
|
|
83
|
+
* @returns Success status
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* ```typescript
|
|
87
|
+
* // Show confirmation dialog first!
|
|
88
|
+
* const confirmed = window.confirm('Are you sure? This cannot be undone.')
|
|
89
|
+
* if (confirmed) {
|
|
90
|
+
* const result = await deleteAccount()
|
|
91
|
+
* if (result.success) {
|
|
92
|
+
* // Redirect to goodbye page or login
|
|
93
|
+
* router.push('/goodbye')
|
|
94
|
+
* }
|
|
95
|
+
* }
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
98
|
+
export declare function deleteAccount(): Promise<EntityActionVoidResult>;
|
|
99
|
+
//# sourceMappingURL=user.actions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user.actions.d.ts","sourceRoot":"","sources":["../../../src/lib/actions/user.actions.ts"],"names":[],"mappings":"AAoCA,OAAO,KAAK,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAA;AAMzE;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAMD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,aAAa,CACjC,IAAI,EAAE,iBAAiB,GACtB,OAAO,CAAC,kBAAkB,CAAC,mBAAmB,CAAC,CAAC,CA2DlD;AAMD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,YAAY,CAChC,QAAQ,EAAE,QAAQ,GACjB,OAAO,CAAC,kBAAkB,CAAC,mBAAmB,CAAC,CAAC,CAsElD;AAMD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,sBAAsB,CAAC,CAsDrE"}
|