Skip to content

Commit f4dccbe

Browse files
committed
Add GroupAccumulatorsPanel to GroupFormPage for enhanced group management
1 parent 496ef33 commit f4dccbe

4 files changed

Lines changed: 795 additions & 1 deletion

File tree

src/components/routes/groups/GroupFormPage.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import {
3434
import GroupFormAssociationsPanel from "./components/GroupFormAssociationsPanel";
3535
import GroupImageField from "./components/GroupImageField";
3636
import { GroupPageShell } from "./components/GroupPageShell";
37+
import GroupAccumulatorsPanel from "./components/GroupAccumulatorsPanel";
3738
import GroupMembersPanel from "./components/GroupMembersPanel";
3839
import { useUserInfo } from "@/hooks/useUserInfo";
3940
import {
@@ -620,7 +621,8 @@ const GroupFormPage = () => {
620621
</div>
621622

622623
{!isNew && groupData && (
623-
<div className="border-t border-dashed border-gray-300 dark:border-input p-4 sm:p-8 pb-12">
624+
<div className="border-t border-dashed border-gray-300 dark:border-input p-4 sm:p-8 pb-12 space-y-10">
625+
<GroupAccumulatorsPanel groupId={groupData.id} groupRole={myRole} />
624626
<GroupMembersPanel
625627
groupId={groupData.id}
626628
members={groupData.members ?? []}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import axiosInstance from "@/config/axios-config";
2+
import type { FkOption } from "../components/FkMultiSearchSelector";
3+
4+
interface PresetMantraDTO {
5+
id: string;
6+
mantra: string;
7+
title?: string | null;
8+
pronunciation?: string | null;
9+
}
10+
11+
interface PublicAccumulatorDTO {
12+
id: string;
13+
target_count?: number | null;
14+
mantra?: PresetMantraDTO | null;
15+
metadata?: { language: string; name: string }[];
16+
}
17+
18+
interface PublicAccumulatorsResponse {
19+
accumulators: PublicAccumulatorDTO[];
20+
total: number;
21+
skip: number;
22+
limit: number;
23+
}
24+
25+
function presetLabel(preset: PublicAccumulatorDTO): string {
26+
const mantraTitle = preset.mantra?.title?.trim();
27+
if (mantraTitle) return mantraTitle;
28+
const metaName = preset.metadata?.[0]?.name?.trim();
29+
if (metaName) return metaName;
30+
const mantraText = preset.mantra?.mantra?.trim();
31+
if (mantraText) {
32+
return mantraText.length > 60 ? `${mantraText.slice(0, 57)}…` : mantraText;
33+
}
34+
return "Untitled preset";
35+
}
36+
37+
export async function searchAccumulatorPresets(params: {
38+
search?: string;
39+
skip?: number;
40+
limit?: number;
41+
}): Promise<{
42+
items: FkOption[];
43+
skip: number;
44+
limit: number;
45+
total: number;
46+
}> {
47+
const skip = params.skip ?? 0;
48+
const limit = params.limit ?? 20;
49+
const { data } = await axiosInstance.get<PublicAccumulatorsResponse>(
50+
`/api/v1/accumulators/presets`,
51+
{
52+
params: {
53+
skip,
54+
limit,
55+
...(params.search?.trim() && { search: params.search.trim() }),
56+
},
57+
},
58+
);
59+
60+
return {
61+
items: data.accumulators.map((preset) => ({
62+
id: preset.id,
63+
title: presetLabel(preset),
64+
image_url: undefined,
65+
})),
66+
skip: data.skip,
67+
limit: data.limit,
68+
total: data.total,
69+
};
70+
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import axiosInstance from "@/config/axios-config";
2+
3+
export interface GroupAccumulatorImage {
4+
thumbnail: string;
5+
medium: string;
6+
original: string;
7+
}
8+
9+
export interface GroupAccumulatorDTO {
10+
id: string;
11+
accumulator_id: string | null;
12+
group_id: string;
13+
title: string | null;
14+
image: GroupAccumulatorImage | null;
15+
image_key: string | null;
16+
target_count: number | null;
17+
start_date: string | null;
18+
end_date: string | null;
19+
created_at: string;
20+
updated_at: string | null;
21+
}
22+
23+
export interface GroupAccumulatorsResponse {
24+
accumulators: GroupAccumulatorDTO[];
25+
total: number;
26+
skip: number;
27+
limit: number;
28+
}
29+
30+
export interface CreateGroupAccumulatorRequest {
31+
accumulator_id?: string | null;
32+
title?: string | null;
33+
image_key?: string | null;
34+
target_count?: number | null;
35+
start_date?: string | null;
36+
end_date?: string | null;
37+
}
38+
39+
export type UpdateGroupAccumulatorRequest = CreateGroupAccumulatorRequest;
40+
41+
const getAuthHeaders = () => ({
42+
Authorization: `Bearer ${sessionStorage.getItem("accessToken")}`,
43+
});
44+
45+
export function resolveGroupAccumulatorImageUrl(
46+
accumulator: Pick<GroupAccumulatorDTO, "image">,
47+
): string | null {
48+
const image = accumulator.image;
49+
if (!image) return null;
50+
return image.medium || image.thumbnail || image.original || null;
51+
}
52+
53+
export const fetchGroupAccumulators = async (
54+
groupId: string,
55+
params?: { skip?: number; limit?: number },
56+
): Promise<GroupAccumulatorsResponse> => {
57+
const { data } = await axiosInstance.get<GroupAccumulatorsResponse>(
58+
`/api/v1/cms/groups/${groupId}/accumulators`,
59+
{
60+
headers: getAuthHeaders(),
61+
params: {
62+
skip: params?.skip ?? 0,
63+
limit: params?.limit ?? 100,
64+
},
65+
},
66+
);
67+
return data;
68+
};
69+
70+
export const createGroupAccumulator = async (
71+
groupId: string,
72+
payload: CreateGroupAccumulatorRequest,
73+
): Promise<GroupAccumulatorDTO> => {
74+
const { data } = await axiosInstance.post<GroupAccumulatorDTO>(
75+
`/api/v1/cms/groups/${groupId}/accumulators`,
76+
payload,
77+
{ headers: getAuthHeaders() },
78+
);
79+
return data;
80+
};
81+
82+
export const updateGroupAccumulator = async (
83+
groupId: string,
84+
groupAccumulatorId: string,
85+
payload: UpdateGroupAccumulatorRequest,
86+
): Promise<GroupAccumulatorDTO> => {
87+
const { data } = await axiosInstance.put<GroupAccumulatorDTO>(
88+
`/api/v1/cms/groups/${groupId}/accumulators/${groupAccumulatorId}`,
89+
payload,
90+
{ headers: getAuthHeaders() },
91+
);
92+
return data;
93+
};
94+
95+
export const deleteGroupAccumulator = async (
96+
groupId: string,
97+
groupAccumulatorId: string,
98+
): Promise<void> => {
99+
await axiosInstance.delete(
100+
`/api/v1/cms/groups/${groupId}/accumulators/${groupAccumulatorId}`,
101+
{ headers: getAuthHeaders() },
102+
);
103+
};

0 commit comments

Comments
 (0)