Skip to content

Commit 926dcde

Browse files
committed
Progress
1 parent 71395f4 commit 926dcde

File tree

8 files changed

+67
-84
lines changed

8 files changed

+67
-84
lines changed

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -202,9 +202,11 @@ public async Task<Git> UpdateRepository(Guid id, GitRepoPatchRequestDTO DTO)
202202

203203
public async Task<Git> CreateRepository(GitRepoPostRequestDTO DTO, OwnerKind OwnerType)
204204
{
205-
GetUserID();
205+
var user = await _context.Users.FindAsync(GetUserID());
206+
SetWebauthHeader("w2wizard");
207+
206208
var response = await _client.PostAsJsonAsync($"/api/v1/repos/{_gitTemplate}/generate", DTO);
207-
// logger.LogInformation("Response: {response}", response);
209+
logger.LogInformation("Response: {response}", response);
208210

209211
if (response.StatusCode is HttpStatusCode.Conflict)
210212
throw new ServiceException(StatusCodes.Status409Conflict, "The repository with the same name already exists");

backend/NXTBackend.API.Models/Requests/Project/ProjectPostRequestDto.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,6 @@ public class ProjectPostRequestDTO : BaseRequestDTO
6565
/// <summary>
6666
/// Tags for the project
6767
/// /// </summary>
68-
[MaxLength(24), StringLengthEnumerable(1, 64), JsonIgnore]
68+
[MaxLength(42)]
6969
public string[] Tags { get; set; }
7070
}

backend/NXTBackend.API/Controllers/ProjectController.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
using NXTBackend.API.Infrastructure.Database;
1919
using NXTBackend.API.Models;
2020
using NXTBackend.API.Models.Requests.Cursus;
21+
using NXTBackend.API.Models.Requests.ExternalGit;
2122
using NXTBackend.API.Models.Requests.LearningGoal;
2223
using NXTBackend.API.Models.Requests.Project;
2324
using NXTBackend.API.Models.Responses.Objects;
@@ -73,7 +74,7 @@ public async Task<ActionResult<ProjectDO>> Create([FromBody] ProjectPostRequestD
7374
if (owner is null)
7475
return UnprocessableEntity("Non-existing user");
7576

76-
var git = await gitService.CreateRepository(new()
77+
var git = await gitService.CreateRepository(new GitRepoPostRequestDTO
7778
{
7879
Name = data.Name,
7980
Description = data.Description,

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5367,6 +5367,7 @@ export interface components {
53675367
thumbnailUrl: string;
53685368
public: boolean;
53695369
enabled: boolean;
5370+
tags?: string[];
53705371
};
53715372
ReviewDO: {
53725373
/** Format: uuid */

frontend/src/lib/components/tag-input.svelte

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,34 +4,33 @@
44
import Button from "./ui/button/button.svelte";
55
import Input from "./ui/input/input.svelte";
66
7-
// let items = $state.raw<string[]>(["1", "2", "3", "4", "5"]);
8-
9-
interface Props {
10-
items: string[]
11-
}
12-
13-
let { items = $bindable() }: Props = $props();
7+
let items = $state.raw<string[]>(["1", "2", "3", "4", "5"]);
148
</script>
159

16-
<div class="flex flex-wrap items-center gap-2 p-2 rounded-md border border-input bg-background min-h-10">
10+
<div
11+
class="border-input bg-background flex min-h-10 flex-wrap items-center gap-2 rounded-md border p-2"
12+
>
1713
{#each items as item, i}
18-
<Badge color="primary" class="shadow-text px-3 py-1 flex gap-1 text-center justify-center items-center">
14+
<Badge
15+
color="primary"
16+
class="shadow-text flex items-center justify-center gap-1 px-3 py-1 text-center"
17+
>
1918
<input type="hidden" name="tags" value={item} />
2019
<span>{item}</span>
2120
<button
2221
type="button"
23-
class="ml-1 rounded-full hover:bg-primary-foreground/20 transition-colors"
24-
onclick={() => items = items.filter((_, j) => j !== i)}
22+
class="hover:bg-primary-foreground/20 ml-1 rounded-full transition-colors"
23+
onclick={() => (items = items.filter((_, j) => j !== i))}
2524
aria-label="Remove tag"
2625
>
27-
<X class="w-4 h-4" />
26+
<X class="h-4 w-4" />
2827
</button>
2928
</Badge>
3029
{/each}
3130
<Input
3231
placeholder="Add tag..."
3332
maxlength={64}
34-
class="border-none bg-transparent shadow-none h-min focus-visible:ring-0 p-0 w-auto min-w-32"
33+
class="h-min w-auto min-w-32 border-none bg-transparent p-0 shadow-none focus-visible:ring-0"
3534
onkeydown={(e) => {
3635
if (e.key === "Enter") {
3736
e.preventDefault();

frontend/src/routes/(app)/new/project/+page.server.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ export const load: PageServerLoad = async ({ request, locals, url }) => {
101101
export const actions: Actions = {
102102
create: async ({ locals, request }) => {
103103
const form = await request.formData();
104+
const session = await locals.session() ?? kitError(403);
104105
let [image, issue] = await ensure(formValueToS3<FormBundle>(form, "thumbnailUrl"));
105106

106107
logger.debug("Tags =>", form.getAll("tags"));
@@ -127,7 +128,8 @@ export const actions: Actions = {
127128
maxMembers: Number(form.get("maxMembers")),
128129
public: form.get("public")?.toString() === "true",
129130
enabled: form.get("enabled")?.toString() === "true",
130-
tags: form.getAll("tags").flatMap((v) => v.toString()),
131+
ownerId: session.user_id, // TODO: Support organizations
132+
tags: [],
131133
thumbnailUrl: url,
132134
},
133135
});

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

Lines changed: 33 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -9,61 +9,50 @@ import { Constants, decodeID } from "$lib/utils";
99
import { logger } from "$lib/logger";
1010
import type { FetchResponse } from "openapi-fetch";
1111
import { check } from "$lib/utils/check.svelte";
12+
import {z} from "zod/v4";
13+
import {useQuery} from "$lib/utils/url.svelte";
1214

1315
// ============================================================================
1416

15-
async function getUserProjects(
16-
locals: App.Locals,
17-
params: URLSearchParams,
18-
userID: string,
19-
) {
20-
const name = params.get("search");
21-
const page = Number(params.get("page") ?? 0);
22-
const { data } = await check(locals.api.GET("/users/{id}/projects", {
23-
params: {
24-
path: { id: userID },
25-
query: {
26-
Size: 10,
27-
Page: !isNaN(page) ? page : undefined,
28-
"filter[name]": name || undefined,
17+
const schema = z.object({
18+
page: z.coerce.number().optional(),
19+
size: z.coerce.number().optional(),
20+
search: z.string().optional(),
21+
filter: z.enum(["available", "subscribed"]).optional(),
22+
});
23+
24+
export type QueryKeys = keyof z.infer<typeof schema>;
25+
26+
export const load: PageServerLoad = async ({ locals, url, params, parent }) => {
27+
const query = useQuery(url, schema);
28+
if (query.read("filter") === "subscribed") {
29+
const { data } = await check(locals.api.GET("/users/{id}/projects", {
30+
params: {
31+
path: { id: decodeID(params.id) },
32+
query: {
33+
Size: query.read("size"),
34+
Page: query.read("page"),
35+
"filter[name]": query.read("search")
36+
},
2937
},
30-
},
31-
}))
38+
}))
3239

33-
return data!;
34-
}
40+
return {
41+
projects: data ?? []
42+
}
43+
}
3544

36-
async function getProjects(locals: App.Locals, params: URLSearchParams) {
37-
const name = params.get("search");
38-
const page = Number(params.get("page") ?? 0);
39-
const slug = params.get("slug");
4045
const { data } = await check(locals.api.GET("/projects", {
4146
params: {
4247
query: {
43-
Size: Constants.PER_PAGE,
44-
Page: !isNaN(page) ? page : undefined,
45-
"filter[slug]": slug || undefined,
46-
"filter[name]": name || undefined,
48+
Size: query.read("size"),
49+
Page: query.read("page"),
50+
"filter[name]": query.read("search")
4751
},
4852
},
49-
}));
50-
51-
return data!;
52-
}
53-
54-
55-
// ============================================================================
53+
}))
5654

57-
export const load: PageServerLoad = async ({ locals, url, params, parent }) => {
58-
const parentData = await parent();
59-
const subscribed = url.searchParams.get("subscribed") === "true";
60-
if (subscribed || !parentData.isCurrentUser) {
61-
return {
62-
projects: await getUserProjects(locals, url.searchParams, decodeID(params.id)),
63-
};
64-
} else {
65-
return {
66-
projects: await getProjects(locals, url.searchParams),
67-
};
55+
return {
56+
projects: data ?? []
6857
}
6958
};

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

Lines changed: 11 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
import Archive from "lucide-svelte/icons/archive";
99
import Separator from "$lib/components/ui/separator/separator.svelte";
1010
import * as Tabs from "$lib/components/ui/tabs/index";
11-
import { useQuery } from "$lib/utils/query.svelte";
11+
import { useQuery } from "$lib/utils/url.svelte";
12+
1213
import { z } from "zod";
1314
import Base from "$lib/components/base.svelte";
1415
@@ -17,24 +18,11 @@
1718
import type { PageProps } from "./$types";
1819
import Empty from "$lib/components/empty.svelte";
1920
import * as Alert from "$lib/components/ui/alert";
21+
import type { QueryKeys } from "./+page.server";
2022
2123
const { data }: PageProps = $props();
2224
const debounce = useDebounce();
23-
const query = useQuery(
24-
z.object({
25-
page: z.number().default(0),
26-
search: z.string().optional(),
27-
subscribed: z.boolean().default(true).optional(),
28-
}),
29-
);
30-
31-
function searchProject(search: string) {
32-
if (search.length > 0) {
33-
query.write("search", search);
34-
} else {
35-
query.write("search", undefined);
36-
}
37-
}
25+
const query = useQuery<QueryKeys>(page.url);
3826
</script>
3927

4028
<svelte:head>
@@ -49,13 +37,14 @@
4937
icon={Search}
5038
value={query.read("search")}
5139
placeholder="Search for cursus"
52-
oninput={(v) => debounce(searchProject, v.currentTarget.value.trim())}
40+
oninput={(v) =>
41+
debounce((q: string) => query.write("filter", q), v.currentTarget.value.trim())}
5342
/>
5443
{#if data.session && data.isCurrentUser}
5544
<Separator />
5645
<Tabs.Root
57-
value={query.read("subscribed") ? "subscribed" : "all"}
58-
onValueChange={(v) => query.write("subscribed", v === "subscribed")}
46+
value={query.read("filter") ?? "subscribed"}
47+
onValueChange={(v) => query.write("filter", v)}
5948
>
6049
<Tabs.List class="w-full">
6150
<Tabs.Trigger class="w-full" value="subscribed">Subscribed</Tabs.Trigger>
@@ -76,14 +65,14 @@
7665
{#if data.projects.length === 0}
7766
<Empty />
7867
{/if}
79-
{#key query.read("subscribed")}
80-
{#if query.read("subscribed") === true || !data.isCurrentUser}
68+
{#key query.read("filter")}
69+
{#if query.read("filter") === "subscribed" || !data.isCurrentUser}
8170
{#each data.projects as up}
8271
{@const userProject = up as BackendTypes["UserProjectDO"]}
8372
<Taskcard
8473
href="projects/{userProject.project?.slug}"
8574
type="project"
86-
title={userProject.project?.name}
75+
title={userProject.project?.name ?? "Unknown"}
8776
state={userProject.state}
8877
/>
8978
{/each}

0 commit comments

Comments
 (0)