Skip to content
Merged
Show file tree
Hide file tree
Changes from 40 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
2fd895b
✨ feat: waiting list (backend)
Strehk Sep 27, 2025
448018d
🧹 chore: Dev Tooling in layout
Strehk Sep 27, 2025
45bdd63
✨ feat: waiting list
Strehk Sep 28, 2025
53ad2da
Update src/lib/components/ConferenceCard/ConferenceCard.svelte
Strehk Sep 30, 2025
400e1a1
🚀 Enhancement: Better Registration Status Indicators
Strehk Sep 29, 2025
eb7bf9c
✨ feat: waiting list WIP
Strehk Sep 30, 2025
5a0a373
🚀 Enhancement: Add seat amount to custom conference roles
Strehk Sep 30, 2025
3d2f0dd
make linter stage files automatically
Strehk Sep 30, 2025
9efdeff
✨ feat: waiting list form
Strehk Sep 30, 2025
984c144
🚀 Enhancement: Update .trivyignore to include additional vulnerabilities
Strehk Oct 5, 2025
5aafbfc
Merge branch 'main' into waiting-list
Strehk Oct 18, 2025
4d7d31f
✨ feat: Waiting List
Strehk Oct 19, 2025
c82a9fd
✨ feat: Waiting List
Strehk Oct 20, 2025
d8a1317
Merge branch 'main' into waiting-list
Strehk Oct 20, 2025
02b33ce
Update Lockfile
Strehk Oct 20, 2025
76b45ec
🧹 chore: update sveltekit-superforms
Strehk Oct 20, 2025
19985bc
CodeRabbit Suggestions
Strehk Oct 20, 2025
bd75733
Add frontend validation
Strehk Oct 22, 2025
ce00d8f
fix small check
Strehk Oct 23, 2025
e84a29a
wip
Strehk Oct 23, 2025
cea994c
Merge branch 'main' into 269-assignment-assistant-make-compatible-wit…
Strehk Oct 23, 2025
f1423a3
enhancement: Add time display to registration deadline
Strehk Oct 26, 2025
98cb33f
feat: Registration Grace Period
Strehk Oct 26, 2025
5345dec
Merge branch 'main' into 269-assignment-assistant-make-compatible-wit…
Strehk Oct 27, 2025
9795ca6
wip
Strehk Oct 28, 2025
581bb34
wip
Strehk Oct 29, 2025
2e57cca
wip
Strehk Oct 29, 2025
e5d67d7
wip
Strehk Oct 29, 2025
aacf672
Merge branch 'main' into 269-assignment-assistant-make-compatible-wit…
Strehk Oct 29, 2025
7d46e70
wip
Strehk Oct 29, 2025
1a104b2
Code Rabbit Suggestions
Strehk Oct 29, 2025
c02a77c
Code Rabbit Suggestions
Strehk Oct 29, 2025
4c1692e
New assistant improvements
Strehk Oct 30, 2025
47cd30a
New assistant improvements
Strehk Oct 30, 2025
65ad6ac
Enhancement: Big Assignment Assistant Overhaul, including compatibili…
Strehk Oct 29, 2025
654a089
Merge branch 'main' into 269-assignment-assistant-make-compatible-wit…
Strehk Oct 30, 2025
bc5840b
Format
Strehk Oct 30, 2025
5e08bf6
New Conference Query and Mutation Endpoints
Strehk Oct 30, 2025
42bb289
Nitpicks
Strehk Oct 30, 2025
0fc3ade
WIP
Strehk Oct 30, 2025
d9d3c79
Normalize Schools Feature
Strehk Nov 1, 2025
7c97f33
Linting
Strehk Nov 1, 2025
6c126c0
Move return into transaction
Strehk Nov 1, 2025
8cd332e
Update Trivyignore
Strehk Nov 1, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions messages/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,7 @@
"end": "Ende",
"enterCode": "Code eingeben",
"enterDateOfdateReceipt": "Empfangsdatum eingeben",
"enterNewSchoolName": "Neuen Schulnamen eingeben",
"entryCode": "Eintrittscode",
"experience": "Erfahrung",
"exportFrom": "Export aus {appName}",
Expand Down
7 changes: 7 additions & 0 deletions schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,7 @@ type Conference {
postalStreet: String
postalZip: String
registrationDeadlineGracePeriodMinutes: Int!
schools: [ConferenceSchools!]!
singleParticipants: [SingleParticipant!]!
startAssignment: DateTime!
startConference: DateTime!
Expand Down Expand Up @@ -648,6 +649,11 @@ input ConferenceScalarRelationFilter {
isNot: ConferenceWhereInput
}

type ConferenceSchools {
count: Int!
school: String!
}

enum ConferenceState {
ACTIVE
PARTICIPANT_REGISTRATION
Expand Down Expand Up @@ -1974,6 +1980,7 @@ type Mutation {
deleteOneTeamMember(where: TeamMemberWhereUniqueInput!): TeamMember
deleteOneUser(where: UserWhereUniqueInput!): User
deleteOneWaitingListEntry(where: WaitingListEntryWhereUniqueInput!): WaitingListEntry
normalizeSchoolsInConference(conferenceId: String!, newSchoolName: String!, schoolsToMerge: [String!]!): Conference
rotateSupervisorConnectionCode(id: ID!): ConferenceSupervisor!
seedNewConference(data: JSONObject!): SeedNewConferenceResult!
sendAssignmentData(data: JSONObject!, where: ConferenceWhereUniqueInput!): SetAssignmentDataResult!
Expand Down
115 changes: 114 additions & 1 deletion src/api/resolvers/modules/conference/conference.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ import { toDataURL } from '$api/services/fileToDataURL';
import { db } from '$db/db';
import { conferenceSettingsFormSchema } from '../../../../routes/(authenticated)/management/[conferenceId]/configuration/form-schema';
import { ConferenceState } from '$db/generated/graphql/inputs';
import { conference } from '$lib/paraglide/messages';
import { findManyNationQueryObject } from '$db/generated/graphql/Nation';

builder.prismaObject('Conference', {
Expand Down Expand Up @@ -215,6 +214,70 @@ builder.prismaObject('Conference', {
conferenceId: conference.id
}
})
}),
schools: t.field({
type: [
builder.simpleObject('ConferenceSchools', {
fields: (t) => ({
school: t.string(),
count: t.int()
})
})
],
resolve: async (conference, _, ctx) => {
const delegations = await db.delegation.groupBy({
where: {
conferenceId: conference.id,
school: {
not: null
},
applied: {
equals: true
},
AND: [ctx.permissions.allowDatabaseAccessTo('list').Delegation]
},
by: 'school',
_count: {
school: true
}
});

const singleParticipants = await db.singleParticipant.groupBy({
where: {
conferenceId: conference.id,
school: {
not: null
},
applied: {
equals: true
},
AND: [ctx.permissions.allowDatabaseAccessTo('list').SingleParticipant]
},
by: 'school',
_count: {
school: true
}
});

const res: { school: string; count: number }[] = [];

for (const delegation of delegations) {
if (!delegation.school) continue;
res.push({ school: delegation.school, count: delegation._count.school });
}

for (const singleParticipant of singleParticipants) {
if (!singleParticipant.school) continue;
const existing = res.find((r) => r.school === singleParticipant.school);
if (existing) {
existing.count += singleParticipant._count.school;
} else {
res.push({ school: singleParticipant.school, count: singleParticipant._count.school });
}
}

return res;
}
})
})
});
Expand Down Expand Up @@ -500,3 +563,53 @@ builder.mutationFields((t) => {
})
};
});

builder.mutationFields((t) => {
const field = updateOneConferenceMutationObject(t);
return {
normalizeSchoolsInConference: t.prismaField({
...field,
args: {
conferenceId: t.arg.string({ required: true }),
schoolsToMerge: t.arg.stringList(),
newSchoolName: t.arg.string({ required: true })
},
resolve: async (query, root, args, ctx, info) => {
await db.$transaction(async (tx) => {
const { conferenceId, schoolsToMerge } = args;

await tx.delegation.updateMany({
where: {
conferenceId,
school: {
in: schoolsToMerge
},
AND: [ctx.permissions.allowDatabaseAccessTo('update').Delegation]
},
data: {
school: args.newSchoolName
}
});

await tx.singleParticipant.updateMany({
where: {
conferenceId,
school: {
in: schoolsToMerge
},
AND: [ctx.permissions.allowDatabaseAccessTo('update').SingleParticipant]
},
data: {
school: args.newSchoolName
}
});
});
return await db.conference.findUnique({
where: {
id: args.conferenceId
}
});
}
})
};
});
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import { goto } from '$app/navigation';
import { onMount } from 'svelte';
import { RAW_DATA_KEY } from './local_storage_keys';
import { graphql } from '$houdini';

let projects: {
id: string;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
<script lang="ts">
import { graphql } from '$houdini';
import StarRating from '$lib/components/StarRating.svelte';
import { getAgeAtConference } from '$lib/services/ageChecker';
import codenamize from '$lib/services/codenamize';
import formatNames from '$lib/services/formatNames';
import { getFullTranslatedCountryNameFromISO3Code } from '$lib/services/nationTranslationHelper.svelte';
import type { Delegation } from './appData.svelte';
import { getConference, type Delegation } from './appData.svelte';
import LoadingData from './components/LoadingData.svelte';
import { getWeights } from './weights.svelte';

Expand Down Expand Up @@ -70,6 +71,8 @@
id
given_name
family_name
birthday
conferenceParticipationsCount
}
}
`);
Expand Down Expand Up @@ -121,6 +124,22 @@
</p>
<StarRating rating={application.evaluation ?? getWeights().nullRating} size="xs" />
<div class="flex items-center justify-center gap-2 text-xs">
<LoadingData
fetching={$getApplicationDetailsQuery.fetching}
error={$getApplicationDetailsQuery.error}
>
<div class="tooltip" data-tip="Durchschnittsalter">
{(
$getApplicationDetailsQuery.data?.findManyUsers?.reduce((acc, user) => {
const age = getAgeAtConference(
user.birthday,
getConference()?.startConference ?? new Date()
);
return acc + (age ? age : 0);
}, 0) / ($getApplicationDetailsQuery.data?.findManyUsers?.length || 1)
).toFixed(1)}
</div>
</LoadingData>
{#if application.note}
<div class="tooltip" data-tip={application.note}>
<i class="fas fa-sticky-note"></i>
Expand Down Expand Up @@ -182,6 +201,18 @@
<i class="fas fa-split"></i>
</div>
{/if}
<LoadingData
fetching={$getApplicationDetailsQuery.fetching}
error={$getApplicationDetailsQuery.error}
>
<div class="tooltip" data-tip="Durchschnittliche Konferenzteilnahmen">
{(
$getApplicationDetailsQuery.data?.findManyUsers?.reduce((acc, user) => {
return acc + (user.conferenceParticipationsCount ?? 0);
}, 0) / ($getApplicationDetailsQuery.data?.findManyUsers?.length || 1)
).toFixed(1)}
</div>
</LoadingData>
</div>
</div>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ export const DelegationSchema = z.object({
id: z.string(),
appliedForRoles: z.array(AppliedForDelegationRoleSchema),
members: z.array(MemberSchema),
school: z.string().optional(),
supervisors: z.undefined(),
user: z.undefined(),
splittedFrom: z.string().nullish(),
Expand All @@ -108,6 +109,7 @@ export const SingleParticipantSchema = z.object({
id: z.string(),
user: UserSchema,
appliedForRoles: z.array(AppliedForSingleRoleSchema),
school: z.string().optional(),
supervisors: z.optional(z.array(SupervisorSchema)),
members: z.undefined(),
splittedFrom: z.undefined(),
Expand Down Expand Up @@ -180,7 +182,34 @@ export const getConference: () => Conference | undefined = () => {
export const getApplications = () => {
const project = getProject();
if (!project) return [];
return [...project.data.delegations, ...project.data.singleParticipants];
return [
...project.data.delegations.toSorted((a, b) => {
if (a.members?.length !== b.members?.length) {
return b.members?.length - a.members?.length;
}
return a.id.localeCompare(b.id);
}),
...project.data.singleParticipants
];
};

export const getSchools = () => {
const applications = getApplications();
const schools: { school: string; count: number; members: number }[] = [];
for (const application of applications) {
const schoolEntry = schools.find((x) => x.school === application.school);
if (schoolEntry) {
schoolEntry.count += 1;
schoolEntry.members += application?.members?.length ?? 1;
} else {
schools.push({
school: application.school ?? 'No School',
count: 1,
members: application?.members?.length ?? 1
});
}
}
return schools.toSorted((a, b) => a.school.localeCompare(b.school));
};

export const getDelegationApplications = () => {
Expand Down Expand Up @@ -258,9 +287,9 @@ export const getMoreInfoLink = (id: string) => {
const project = getProject();
if (!project) return '';
if (project.data.singleParticipants.find((singleParticipant) => singleParticipant.id === id)) {
return `/management/${project.data.conference.id}/individuals?filter=${id}`;
return `/management/${project.data.conference.id}/individuals?selected=${id}`;
}
return `/management/${project.data.conference.id}/delegations?filter=${id}`;
return `/management/${project.data.conference.id}/delegations?selected=${id}`;
};

export const evaluateApplication = (id: string, evaluation: number) => {
Expand Down
Loading
Loading