Skip to content

Commit 71395f4

Browse files
committed
Use id instead
1 parent 4c32bb9 commit 71395f4

File tree

9 files changed

+67
-16
lines changed

9 files changed

+67
-16
lines changed

backend/NXTBackend.API.Core/Services/Implementation/UserCursusService.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ public async Task<CursusTrackDTO> ConstructTrack(Guid userId, Guid cursusId)
6767
// 3. Load user goals separately with minimal data needed
6868
var userGoalsInfo = await _context.UserGoals
6969
.AsNoTracking()
70-
.Where(ug => ug.UserCursusId == userCursus.Id)
70+
.Where(ug => ug.UserId == userId)
7171
.Select(ug => new
7272
{
7373
ug.GoalId,

backend/NXTBackend.API.Core/Services/Implementation/UserGoalService.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ public sealed class UserGoalService : BaseService<UserGoal>, IUserGoalService
1616
public UserGoalService(DatabaseContext ctx) : base(ctx)
1717
{
1818
DefineFilter<Guid>("user_id", (q, id) => q.Where(ug => ug.UserId == id));
19+
DefineFilter<Guid>("goal_id", (q, id) => q.Where(ug => ug.GoalId == id));
1920
DefineFilter<string>("name", (q, name) => q.Where(ug => EF.Functions.Like(ug.Goal.Name, $"%{name}%")));
21+
DefineFilter<string>("slug", (q, slug) => q.Include(ug => ug.Goal).Where(ug => ug.Goal.Slug == slug));
2022
}
2123
}

backend/NXTBackend.API/Controllers/UserController.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -244,15 +244,19 @@ public async Task<IActionResult> GetUserGoals(
244244
Guid id,
245245
[FromQuery] PaginationParams pagination,
246246
[FromQuery] SortingParams sorting,
247-
[FromQuery(Name = "filter[name]"), Description("The name of the learning goal")] string? goalName
247+
[FromQuery(Name = "filter[name]")] string? name,
248+
[FromQuery(Name = "filter[slug]")] string? slug,
249+
[FromQuery(Name = "filter[goal_id]")] string? goalId
248250
)
249251
{
250252
var user = await userService.FindByIdAsync(id);
251253
if (user is null)
252254
return NotFound("User not found");
253255

254256
var filters = new FilterDictionary()
255-
.AddFilter("name", goalName)
257+
.AddFilter("name", name)
258+
.AddFilter("slug", slug)
259+
.AddFilter("goal_id", goalId)
256260
.AddFilter("user_id", id);
257261

258262
var page = await userGoalService.GetAllAsync(pagination, sorting, filters);

frontend/src/lib/api/types.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4386,8 +4386,9 @@ export interface paths {
43864386
"page[size]"?: number;
43874387
sort_by?: string;
43884388
sort?: components["schemas"]["Order"];
4389-
/** @description The name of the learning goal */
43904389
"filter[name]"?: string;
4390+
"filter[slug]"?: string;
4391+
"filter[goal_id]"?: string;
43914392
};
43924393
header?: never;
43934394
path: {

frontend/src/lib/components/pagination.svelte

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
1010
interface Props {
1111
/** Total number of items */
12-
totalItems?: number;
12+
pages?: number;
1313
/** How many per page */
1414
perPage?: number;
1515
/** Current page */
@@ -29,7 +29,7 @@
2929
}
3030
3131
let {
32-
totalItems = 0,
32+
pages = 0,
3333
perPage = $bindable(10),
3434
page = $bindable(1),
3535
variant = "default",
@@ -40,8 +40,7 @@
4040
onPageSizeChange,
4141
}: Props = $props();
4242
43-
const totalPages = $derived(Math.max(1, Math.ceil(totalItems / perPage)));
44-
$inspect(totalPages)
43+
const totalPages = $derived(pages * perPage);
4544
function handlePageSizeChange(newSize: number) {
4645
perPage = newSize;
4746
if (page > totalPages) {
@@ -123,7 +122,7 @@
123122
{:else}
124123
<Pagination.Item>
125124
<span class="px-2 text-sm font-medium">
126-
Page {currentPage} of {totalPages}
125+
Page {currentPage} of {pages.length}
127126
</span>
128127
</Pagination.Item>
129128
{/if}

frontend/src/routes/(app)/users/[id=uuid]/goals/+page.server.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export const load: PageServerLoad = async ({locals, params, url}) => {
4343
logger.debug({ lel: Number(response.headers.get("X-Pages")) })
4444

4545
return {
46-
size: Number(response.headers.get("X-Pages")),
46+
XPages: Number(response.headers.get("X-Pages")),
4747
goals: data ?? []
4848
}
4949
} else {
@@ -61,7 +61,7 @@ export const load: PageServerLoad = async ({locals, params, url}) => {
6161
logger.debug({ lel: Number(response.headers.get("X-Pages")) })
6262

6363
return {
64-
size: Number(response.headers.get("X-Pages")),
64+
XPages: Number(response.headers.get("X-Pages")),
6565
goals: data ?? []
6666
}
6767
}

frontend/src/routes/(app)/users/[id=uuid]/goals/+page.svelte

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,11 @@
1212
import * as Tabs from "$lib/components/ui/tabs/index";
1313
import { page } from "$app/state";
1414
import type { QueryKeys } from "./+page.server";
15+
import { encodeID } from "$lib/utils";
1516
1617
const debounce = useDebounce();
1718
const query = useQuery<QueryKeys>(page.url);
1819
const { data }: PageProps = $props();
19-
20-
const size = $derived(data.size);
2120
</script>
2221

2322
<svelte:head>
@@ -66,7 +65,7 @@
6665
<li>
6766
<Pagination
6867
variant="default"
69-
totalItems={size}
68+
pages={data.XPages}
7069
onPage={(p) => query.write("page", p)}
7170
onPageSizeChange={(s) => query.write("size", s)}
7271
/>
@@ -78,12 +77,12 @@
7877
{#if query.read("filter") === "available"}
7978
{#each data.goals as entity}
8079
{@const goal = entity as BackendTypes["LearningGoalDO"]}
81-
<Taskcard href="goals/1" type="goal" title={goal.name} />
80+
<Taskcard href="goals/{encodeID(goal.id)}" type="goal" title={goal.name} />
8281
{/each}
8382
{:else}
8483
{#each data.goals as entity}
8584
{@const goal = entity as BackendTypes["UserGoalDO"]}
86-
<Taskcard href="goals/1" type="goal" title={goal.name ?? "Unknown"} />
85+
<Taskcard href="goals/{encodeID(goal.goalId)}" type="goal" title={goal.name ?? "Unknown"} />
8786
{/each}
8887
{/if}
8988
</div>
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { decodeID } from '$lib/utils';
2+
import { check } from '$lib/utils/check.svelte';
3+
import { error } from '@sveltejs/kit';
4+
import type { PageServerLoad, RouteParams } from './$types';
5+
import { logger } from '$lib/logger';
6+
7+
async function fetchGoal(locals: App.Locals, goalId: GUID) {
8+
const { response, data } = await check(locals.api.GET("/goals", {
9+
params: {
10+
query: {
11+
"filter[id]": decodeID(goalId),
12+
}
13+
}
14+
}));
15+
16+
return data?.at(0) ?? error(404);
17+
}
18+
19+
async function fetchUserGoal(locals: App.Locals, goalId: GUID, userId: GUID) {
20+
const { response, data } = await locals.api.GET("/users/{id}/goals", {
21+
params: {
22+
path: { id: userId },
23+
query: {
24+
"filter[goal_id]": decodeID(goalId),
25+
}
26+
}
27+
});
28+
29+
if (!response.ok && response.status !== 404)
30+
error(response.status, `Failed to fetch user goal: ${response.statusText}`);
31+
return data?.at(0) ?? null;
32+
}
33+
34+
export const load: PageServerLoad = async ({ url, params, locals }) => {
35+
const goalId = decodeID(params.goal);
36+
logger.info(`Loading goal with ID: ${goalId} for user with ID: ${params.id}`, { url: url.pathname });
37+
const [goal, userGoal] = await Promise.all([
38+
fetchGoal(locals, goalId),
39+
fetchUserGoal(locals, goalId, decodeID(params.id))
40+
]);
41+
42+
return {
43+
goal,
44+
userGoal,
45+
};
46+
};

frontend/src/routes/(app)/users/[id=uuid]/goals/[goal=slug]/+page.svelte renamed to frontend/src/routes/(app)/users/[id=uuid]/goals/[goal=uuid]/+page.svelte

File renamed without changes.

0 commit comments

Comments
 (0)