diff --git a/src/actions/groups/interestGroups/create.ts b/src/actions/groups/interestGroups/create.ts
new file mode 100644
index 000000000..3ff6d0fb5
--- /dev/null
+++ b/src/actions/groups/interestGroups/create.ts
@@ -0,0 +1,5 @@
+'use server'
+import { Action } from '@/actions/Action'
+import { InterestGroups } from '@/services/groups/interestGroups'
+
+export const createInterestGroupAction = Action(InterestGroups.create)
diff --git a/src/actions/groups/interestGroups/destroy.ts b/src/actions/groups/interestGroups/destroy.ts
new file mode 100644
index 000000000..66b15e038
--- /dev/null
+++ b/src/actions/groups/interestGroups/destroy.ts
@@ -0,0 +1,5 @@
+'use server'
+import { ActionNoData } from '@/actions/Action'
+import { InterestGroups } from '@/services/groups/interestGroups'
+
+export const destroyInterestGroupAction = ActionNoData(InterestGroups.destroy)
diff --git a/src/actions/groups/interestGroups/read.ts b/src/actions/groups/interestGroups/read.ts
new file mode 100644
index 000000000..7e86b328b
--- /dev/null
+++ b/src/actions/groups/interestGroups/read.ts
@@ -0,0 +1,5 @@
+'use server'
+import { ActionNoData } from '@/actions/Action'
+import { InterestGroups } from '@/services/groups/interestGroups'
+
+export const readInterestGroupsAction = ActionNoData(InterestGroups.readAll)
diff --git a/src/actions/groups/interestGroups/update.ts b/src/actions/groups/interestGroups/update.ts
new file mode 100644
index 000000000..331dc5ecf
--- /dev/null
+++ b/src/actions/groups/interestGroups/update.ts
@@ -0,0 +1,5 @@
+'use server'
+import { Action } from '@/actions/Action'
+import { InterestGroups } from '@/services/groups/interestGroups'
+
+export const updateInterestGroupAction = Action(InterestGroups.update)
diff --git a/src/app/_components/NavBar/navDef.ts b/src/app/_components/NavBar/navDef.ts
index 4e8eaa6dd..cf753edd9 100644
--- a/src/app/_components/NavBar/navDef.ts
+++ b/src/app/_components/NavBar/navDef.ts
@@ -144,7 +144,7 @@ export const itemsForMenu: NavItem[] = [
},
{
name: 'Intressegrupper',
- href: '/infopages/interessegrupper',
+ href: '/interest-groups',
show: 'all',
icon: faGamepad,
},
diff --git a/src/app/interest-groups/CreateInterestGroupForm.module.scss b/src/app/interest-groups/CreateInterestGroupForm.module.scss
new file mode 100644
index 000000000..238fe1438
--- /dev/null
+++ b/src/app/interest-groups/CreateInterestGroupForm.module.scss
@@ -0,0 +1,19 @@
+@use '@/styles/ohma';
+
+.CreateInterestGroupForm {
+ min-width: 50vw;
+ min-height: 50vh;
+ display: grid;
+ place-items: center;
+ form {
+ display: grid;
+ place-items: center;
+ > * {
+ width: 200px;
+ margin: 0;
+ }
+ button[type="submit"] {
+ width: 220px;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/app/interest-groups/CreateInterestGroupForm.tsx b/src/app/interest-groups/CreateInterestGroupForm.tsx
new file mode 100644
index 000000000..ab71826d0
--- /dev/null
+++ b/src/app/interest-groups/CreateInterestGroupForm.tsx
@@ -0,0 +1,20 @@
+import styles from './CreateInterestGroupForm.module.scss'
+import Form from '@/components/Form/Form'
+import { createInterestGroupAction } from '@/actions/groups/interestGroups/create'
+import TextInput from '@/components/UI/TextInput'
+
+export default function CreateInterestGroupForm() {
+ return (
+
+
Lag interessegruppe
+
+
+ )
+}
diff --git a/src/app/interest-groups/InterestGroup.module.scss b/src/app/interest-groups/InterestGroup.module.scss
new file mode 100644
index 000000000..22d9f8546
--- /dev/null
+++ b/src/app/interest-groups/InterestGroup.module.scss
@@ -0,0 +1,16 @@
+@use '@/styles/ohma';
+
+.interestGroup {
+ margin-top: 2rem;
+ border-top: 8px solid ohma.$colors-secondary;
+ padding-top: 1rem;
+ position: relative;
+ h2 {
+ font-size: ohma.$fonts-xl;
+ }
+ .admin {
+ position: absolute;
+ top: 1em;
+ right: 1em;
+ }
+}
\ No newline at end of file
diff --git a/src/app/interest-groups/InterestGroup.tsx b/src/app/interest-groups/InterestGroup.tsx
new file mode 100644
index 000000000..6135be600
--- /dev/null
+++ b/src/app/interest-groups/InterestGroup.tsx
@@ -0,0 +1,80 @@
+import styles from './InterestGroup.module.scss'
+import Form from '@/components/Form/Form'
+import TextInput from '@/components/UI/TextInput'
+import ArticleSection from '@/components/Cms/ArticleSection/ArticleSection'
+import { DestroyInterestGroupAuther, UpdateInterestGroupAuther } from '@/services/groups/interestGroups/Auther'
+import { SettingsHeaderItemPopUp } from '@/components/HeaderItems/HeaderItemPopUp'
+import { updateInterestGroupAction } from '@/actions/groups/interestGroups/update'
+import { destroyInterestGroupAction } from '@/actions/groups/interestGroups/destroy'
+import type { SessionMaybeUser } from '@/auth/Session'
+import type { ExpandedInterestGroup } from '@/services/groups/interestGroups/Types'
+
+type PropTypes = {
+ interestGroup: ExpandedInterestGroup
+ session: SessionMaybeUser
+}
+
+export default function InterestGroup({ interestGroup, session }: PropTypes) {
+ const canUpdate = UpdateInterestGroupAuther.dynamicFields({ groupId: interestGroup.groupId }).auth(session)
+ const canDestroy = DestroyInterestGroupAuther.dynamicFields({}).auth(session)
+
+ const PopUpKey = `Update interest group ${interestGroup.name}`
+
+ return (
+
+
{interestGroup.name}
+
+ {
+ canUpdate.authorized || canDestroy.authorized ? (
+
+ {
+ canUpdate.authorized && (
+ <>
+ Update interest group
+
+ >
+ )
+ }
+ {
+ canDestroy.authorized && (
+
+ )
+ }
+
+ ) : <>>
+ }
+
+
+
+ )
+}
diff --git a/src/app/interest-groups/page.tsx b/src/app/interest-groups/page.tsx
new file mode 100644
index 000000000..3efcdecf8
--- /dev/null
+++ b/src/app/interest-groups/page.tsx
@@ -0,0 +1,34 @@
+import CreateInterestGroupForm from './CreateInterestGroupForm'
+import InterestGroup from './InterestGroup'
+import { readInterestGroupsAction } from '@/actions/groups/interestGroups/read'
+import SpecialCmsParagraph from '@/cms/CmsParagraph/SpecialCmsParagraph'
+import PageWrapper from '@/components/PageWrapper/PageWrapper'
+import { AddHeaderItemPopUp } from '@/components/HeaderItems/HeaderItemPopUp'
+import { CreateInterestGroupAuther } from '@/services/groups/interestGroups/Auther'
+
+export default async function InterestGroups() {
+ const { session, ...interestGroupsRes } = await readInterestGroupsAction.bind(null, {})()
+ if (!interestGroupsRes.success) return Failed to load interest groups
//TODO: Change to unwrap
+ const interestGroups = interestGroupsRes.data
+
+ const canCreate = CreateInterestGroupAuther.dynamicFields({}).auth(session)
+
+ return (
+
+
+
+ )
+ }>
+
+
+ {
+ interestGroups.map(interestGroup => (
+
+ ))
+ }
+
+
+ )
+}
diff --git a/src/auth/auther/RequirePermissionOrGroupAdmin.ts b/src/auth/auther/RequirePermissionOrGroupAdmin.ts
new file mode 100644
index 000000000..98eb052a0
--- /dev/null
+++ b/src/auth/auther/RequirePermissionOrGroupAdmin.ts
@@ -0,0 +1,13 @@
+import { AutherFactory } from './Auther'
+import type { Permission } from '@prisma/client'
+
+export const RequirePermissionOrGroupAdmin = AutherFactory<
+ { permission: Permission },
+ { groupId: number },
+ 'USER_NOT_REQUIERED_FOR_AUTHORIZED'
+>(({ session, staticFields, dynamicFields }) => ({
+ success: session.permissions.includes(staticFields.permission) || session.memberships.some(
+ membersip => membersip.groupId === dynamicFields.groupId && membersip.admin && membersip.active
+ ),
+ session,
+}))
diff --git a/src/prisma/prismaservice/cms_paragraphs/interest_groups/general_info.md b/src/prisma/prismaservice/cms_paragraphs/interest_groups/general_info.md
new file mode 100644
index 000000000..b6c4f4590
--- /dev/null
+++ b/src/prisma/prismaservice/cms_paragraphs/interest_groups/general_info.md
@@ -0,0 +1,11 @@
+Har du en interesse du har lyst til å dele med andre? Interessegrupper er noe vi har startet for at det skal være lettere for deg å samle folk rundt en felles interesse.
+
+**Hva er en interessegruppe?** En interessegruppe er en gruppe med studenter som møtes for å snakke om eller dyrke en interesse. En interessegruppe kan omhandle akkurat det du interesserer deg for som du ønsker å dele med andre. Er det noe du føler mangler av tilbud i studenttilværelsen, er det godt mulig at interessegrupper er noe for deg!
+
+**Hvordan bli en interessegruppe?** Det er meget lett å bli en interessegruppe, bare send en mail til hs@omega.ntnu.no med navn på gruppen, kontaktinformasjon gruppeleder og en kort beskrivelse av hva dere gjør. Hvis dere trenger ekstra utstyr er det mulig å søke støtte fra fondet.
+
+**Ønsker du å opprette en gruppe?** Send en mail til hs@omega.ntnu.no, så tar vi kontakt.
+
+Få støtte! Ønsker din gruppe å søke støtte, send en søknad til scatmeister@omega.ntnu.no. For å få penger må gruppen bestå av flere aktive medlemmer i Omega, samt ha klart et budsjett over hva de planlegger å bruke pengene på. Søknaden vil så bli vurdert av fondsstyret. Det er altså ikke en garanti å få penger, men hvis formålet er noe Broedre iitem systre kan nyte godt av lover det godt for søknaden.
+
+Høres dette vrient ut, så fortvil ikke: Vi er her for å hjelpe dere.
\ No newline at end of file
diff --git a/src/prisma/prismaservice/src/development/seedDevGroups.ts b/src/prisma/prismaservice/src/development/seedDevGroups.ts
index 3bf9f0c66..06a04d3b7 100644
--- a/src/prisma/prismaservice/src/development/seedDevGroups.ts
+++ b/src/prisma/prismaservice/src/development/seedDevGroups.ts
@@ -125,6 +125,13 @@ export default async function seedDevGroups(prisma: PrismaClient) {
data: {
name: `Interessegruppe ${i}`,
shortName: `IG${i}`,
+ articleSection: {
+ create: {
+ cmsImage: { create: {} },
+ cmsParagraph: { create: {} },
+ cmsLink: { create: {} },
+ }
+ },
group: {
create: {
groupType: 'INTEREST_GROUP',
diff --git a/src/prisma/prismaservice/src/seedCmsConfig.ts b/src/prisma/prismaservice/src/seedCmsConfig.ts
index c95490ef5..4f33930e7 100644
--- a/src/prisma/prismaservice/src/seedCmsConfig.ts
+++ b/src/prisma/prismaservice/src/seedCmsConfig.ts
@@ -160,6 +160,10 @@ export const seedSpecialCmsParagraphConfig: CmsParagraphSeedSpecialConfig = {
name: 'frontpage_4_paragraph',
file: 'frontpage/frontpage_4.md'
},
+ INTEREST_GROUP_GENERAL_INFO: {
+ name: 'interest_group_general_info',
+ file: 'interest_groups/general_info.md'
+ },
CAREER_INFO: {
name: 'career_info',
file: 'career/career_info.md'
diff --git a/src/prisma/schema/cms.prisma b/src/prisma/schema/cms.prisma
index cd35030a0..d383e1074 100644
--- a/src/prisma/schema/cms.prisma
+++ b/src/prisma/schema/cms.prisma
@@ -43,7 +43,8 @@ enum SpecialCmsParagraph {
FRONTPAGE_2
FRONTPAGE_3
FRONTPAGE_4
-
+
+ INTEREST_GROUP_GENERAL_INFO
CAREER_INFO
}
@@ -99,10 +100,11 @@ model ArticleSection {
order Int @default(autoincrement()) //The order "position" of the article section in the article
//If true, the article section will be deleted if it is empty, ie has no relation to paragraph, link or image
- destroyOnEmpty Boolean @default(true)
+ destroyOnEmpty Boolean @default(true)
cmsImage CmsImage?
cmsParagraph CmsParagraph?
cmsLink CmsLink?
+ InterestGroup InterestGroup?
@@unique([articleId, order]) //There can only be one article section with a given order in an article
}
diff --git a/src/prisma/schema/group.prisma b/src/prisma/schema/group.prisma
index 42ae83298..8b25be0b2 100644
--- a/src/prisma/schema/group.prisma
+++ b/src/prisma/schema/group.prisma
@@ -83,7 +83,10 @@ model InterestGroup {
name String
shortName String @unique
- // TODOD - add intereset group data
+
+ articleSection ArticleSection @relation(fields: [articleSectionId], references: [id], onDelete: Restrict, onUpdate: Cascade)
+ articleSectionId Int @unique
+ // TODO - add intereset group data
}
model ManualGroup {
diff --git a/src/prisma/schema/permission.prisma b/src/prisma/schema/permission.prisma
index ec7b539e4..f135f8376 100644
--- a/src/prisma/schema/permission.prisma
+++ b/src/prisma/schema/permission.prisma
@@ -41,10 +41,8 @@ enum Permission {
COMMITTEE_DESTROY
// Groups - InterestGroups
- INTEREST_GROUP_CREATE
+ INTEREST_GROUP_ADMIN
INTEREST_GROUP_READ
- INTEREST_GROUP_UPDATE
- INTEREST_GROUP_DESTROY
// Groups - OmegaMembershipGroup
OMEGA_MEMBERSHIP_GROUP_READ
diff --git a/src/services/ServiceMethod.ts b/src/services/ServiceMethod.ts
index aeb1e3b80..991a2d831 100644
--- a/src/services/ServiceMethod.ts
+++ b/src/services/ServiceMethod.ts
@@ -79,10 +79,13 @@ export function ServiceMethod<
return config.withData ? {
withData: true,
client: (prisma) => ({
- execute: ({ data, params, session }, authRunConfig) => {
+ execute: async ({ data, params, session }, authRunConfig) => {
if (authRunConfig.withAuth) {
const authRes = config.auther.dynamicFields(
- config.dynamicFields({
+ config.dynamicFields ? config.dynamicFields({
+ params,
+ data: config.serviceMethodHandler.detailedValidate(data)
+ }) : await config.dynamicFieldsAsync({
params,
data: config.serviceMethodHandler.detailedValidate(data)
})
@@ -99,10 +102,12 @@ export function ServiceMethod<
} satisfies ServiceMethod : {
withData: false,
client: (prisma) => ({
- execute: ({ params, session }, authRunConfig) => {
+ execute: async ({ params, session }, authRunConfig) => {
if (authRunConfig.withAuth) {
const authRes = config.auther.dynamicFields(
- config.dynamicFields({
+ config.dynamicFields ? config.dynamicFields({
+ params
+ }) : await config.dynamicFieldsAsync({
params
})
).auth(
diff --git a/src/services/ServiceTypes.ts b/src/services/ServiceTypes.ts
index 62bd9a24d..552c4b779 100644
--- a/src/services/ServiceTypes.ts
+++ b/src/services/ServiceTypes.ts
@@ -147,6 +147,7 @@ export type ServiceMethodHandlerConfig<
) => Promise,
} : {
withData: false,
+ wantsToOpenTransaction?: WantsToOpenTransaction,
handler: (
prisma: PrismaPossibleTransaction,
params: Params,
@@ -172,8 +173,13 @@ type ServiceMethodHandlerAuthConfig<
DynamicFields extends object,
> = {
auther: AutherStaticFieldsBound
+} & ({
dynamicFields: (dataParams: DynamicFieldsInput) => DynamicFields
-}
+ dynamicFieldsAsync?: never
+} | {
+ dynamicFieldsAsync: (dataParams: DynamicFieldsInput) => Promise
+ dynamicFields?: never
+})
export type ServiceMethodConfig<
WithValidation extends boolean,
diff --git a/src/services/groups/interestGroups/Auther.ts b/src/services/groups/interestGroups/Auther.ts
new file mode 100644
index 000000000..1fbcf299a
--- /dev/null
+++ b/src/services/groups/interestGroups/Auther.ts
@@ -0,0 +1,10 @@
+import { RequirePermission } from '@/auth/auther/RequirePermission'
+import { RequirePermissionOrGroupAdmin } from '@/auth/auther/RequirePermissionOrGroupAdmin'
+
+export const ReadInterestGroupAuther = RequirePermission.staticFields({ permission: 'INTEREST_GROUP_READ' })
+
+export const CreateInterestGroupAuther = RequirePermission.staticFields({ permission: 'INTEREST_GROUP_ADMIN' })
+
+export const UpdateInterestGroupAuther = RequirePermissionOrGroupAdmin.staticFields({ permission: 'INTEREST_GROUP_ADMIN' })
+
+export const DestroyInterestGroupAuther = RequirePermission.staticFields({ permission: 'INTEREST_GROUP_ADMIN' })
diff --git a/src/services/groups/interestGroups/Types.ts b/src/services/groups/interestGroups/Types.ts
index ac25656b9..4ab8df3dd 100644
--- a/src/services/groups/interestGroups/Types.ts
+++ b/src/services/groups/interestGroups/Types.ts
@@ -1,3 +1,6 @@
+import type { ExpandedArticleSection } from '@/services/cms/articleSections/Types'
import type { InterestGroup } from '@prisma/client'
-export type ExpandedInterestGroup = InterestGroup
+export type ExpandedInterestGroup = InterestGroup & {
+ articleSection: ExpandedArticleSection
+}
diff --git a/src/services/groups/interestGroups/create.ts b/src/services/groups/interestGroups/create.ts
index 574546550..e6e1f7a6e 100644
--- a/src/services/groups/interestGroups/create.ts
+++ b/src/services/groups/interestGroups/create.ts
@@ -1,26 +1,31 @@
-import { prismaCall } from '@/services/prismaCall'
-import prisma from '@/prisma'
+import 'server-only'
+import { createInterestGroupValidation } from './validation'
import { readCurrentOmegaOrder } from '@/services/omegaOrder/read'
-import type { ExpandedInterestGroup } from './Types'
+import { ServiceMethodHandler } from '@/services/ServiceMethodHandler'
-type CreateInterestGroupArgs = {
- name: string,
- shortName: string,
-}
+export const create = ServiceMethodHandler({
+ withData: true,
+ validation: createInterestGroupValidation,
+ handler: async (prisma, _, data) => {
+ const { order } = await readCurrentOmegaOrder()
-export async function createInterestGroup({ name, shortName }: CreateInterestGroupArgs): Promise {
- const order = (await readCurrentOmegaOrder()).order
-
- return await prismaCall(() => prisma.interestGroup.create({
- data: {
- name,
- shortName,
- group: {
- create: {
- groupType: 'INTEREST_GROUP',
- order,
+ await prisma.interestGroup.create({
+ data: {
+ ...data,
+ articleSection: {
+ create: {
+ cmsImage: {},
+ cmsParagraph: {},
+ cmsLink: {},
+ }
+ },
+ group: {
+ create: {
+ groupType: 'INTEREST_GROUP',
+ order,
+ }
}
}
- }
- }))
-}
+ })
+ }
+})
diff --git a/src/services/groups/interestGroups/destroy.ts b/src/services/groups/interestGroups/destroy.ts
index be0dfcbef..ef997dd02 100644
--- a/src/services/groups/interestGroups/destroy.ts
+++ b/src/services/groups/interestGroups/destroy.ts
@@ -1,11 +1,17 @@
-import { prismaCall } from '@/services/prismaCall'
-import prisma from '@/prisma'
-import type { ExpandedInterestGroup } from './Types'
+import 'server-only'
+import { ServiceMethodHandler } from '@/services/ServiceMethodHandler'
-export async function destroyInterestGroup(id: number): Promise {
- return await prismaCall(() => prisma.interestGroup.delete({
- where: {
- id,
- },
- }))
-}
+export const destroy = ServiceMethodHandler({
+ withData: false,
+ wantsToOpenTransaction: true,
+ handler: async (prisma, { id }: { id: number }) => {
+ await prisma.$transaction(async tx => {
+ const intrestGroup = await tx.interestGroup.delete({
+ where: { id }
+ })
+ await tx.group.delete({
+ where: { id: intrestGroup.groupId }
+ })
+ })
+ }
+})
diff --git a/src/services/groups/interestGroups/index.ts b/src/services/groups/interestGroups/index.ts
new file mode 100644
index 000000000..3449b3578
--- /dev/null
+++ b/src/services/groups/interestGroups/index.ts
@@ -0,0 +1,54 @@
+import 'server-only'
+import {
+ CreateInterestGroupAuther,
+ DestroyInterestGroupAuther,
+ ReadInterestGroupAuther,
+ UpdateInterestGroupAuther
+} from './Auther'
+import { read, readAll } from './read'
+import { create } from './create'
+import { update } from './update'
+import { destroy } from './destroy'
+import { ServiceMethod } from '@/services/ServiceMethod'
+
+export const InterestGroups = {
+ read: ServiceMethod({
+ withData: false,
+ hasAuther: true,
+ auther: ReadInterestGroupAuther,
+ dynamicFields: () => ({}),
+ serviceMethodHandler: read,
+ }),
+ readAll: ServiceMethod({
+ withData: false,
+ hasAuther: true,
+ auther: ReadInterestGroupAuther,
+ dynamicFields: () => ({}),
+ serviceMethodHandler: readAll,
+ }),
+ create: ServiceMethod({
+ withData: true,
+ hasAuther: true,
+ auther: CreateInterestGroupAuther,
+ dynamicFields: () => ({}),
+ serviceMethodHandler: create,
+ }),
+ update: ServiceMethod({
+ withData: true,
+ hasAuther: true,
+ auther: UpdateInterestGroupAuther,
+ dynamicFieldsAsync: async ({ params }) => ({
+ groupId: (await read.client('NEW').execute(
+ { params: { id: params.id }, session: null }
+ ).then(interestGroup => interestGroup.groupId))
+ }),
+ serviceMethodHandler: update,
+ }),
+ destroy: ServiceMethod({
+ withData: false,
+ hasAuther: true,
+ auther: DestroyInterestGroupAuther,
+ dynamicFields: () => ({}),
+ serviceMethodHandler: destroy
+ })
+} as const
diff --git a/src/services/groups/interestGroups/read.ts b/src/services/groups/interestGroups/read.ts
index ffee51c11..1cd34871f 100644
--- a/src/services/groups/interestGroups/read.ts
+++ b/src/services/groups/interestGroups/read.ts
@@ -1,21 +1,39 @@
-import { prismaCall } from '@/services/prismaCall'
-import prisma from '@/prisma'
+import 'server-only'
+import { articleSectionsRealtionsIncluder } from '@/services/cms/articleSections/ConfigVars'
+import { ServiceMethodHandler } from '@/services/ServiceMethodHandler'
import type { ExpandedInterestGroup } from './Types'
-export async function readInterestGroups(): Promise {
- return await prismaCall(() => prisma.interestGroup.findMany())
-}
-
type ReadInterestGroupArgs = {
id?: number,
shortName?: string,
}
-export async function readInterestGroup({ id, shortName }: ReadInterestGroupArgs): Promise {
- return await prismaCall(() => prisma.interestGroup.findUniqueOrThrow({
+export const readAll = ServiceMethodHandler({
+ withData: false,
+ handler: async (prisma): Promise => prisma.interestGroup.findMany({
+ include: {
+ articleSection: {
+ include: articleSectionsRealtionsIncluder,
+ },
+ },
+ orderBy: [
+ { name: 'asc' },
+ { id: 'asc' },
+ ]
+ })
+})
+
+export const read = ServiceMethodHandler({
+ withData: false,
+ handler: async (prisma, { id, shortName }: ReadInterestGroupArgs) => await prisma.interestGroup.findUniqueOrThrow({
where: {
id,
shortName,
+ },
+ include: {
+ articleSection: {
+ include: articleSectionsRealtionsIncluder,
+ },
}
- }))
-}
+ })
+})
diff --git a/src/services/groups/interestGroups/update.ts b/src/services/groups/interestGroups/update.ts
index 888bfd47e..abf96ee86 100644
--- a/src/services/groups/interestGroups/update.ts
+++ b/src/services/groups/interestGroups/update.ts
@@ -1,17 +1,12 @@
-import { prismaCall } from '@/services/prismaCall'
-import prisma from '@/prisma'
-import type { ExpandedInterestGroup } from './Types'
+import 'server-only'
+import { updateInterestGroupValidation } from './validation'
+import { ServiceMethodHandler } from '@/services/ServiceMethodHandler'
-type CreateInterestGroupArgs = {
- name: string,
- shortName: string,
-}
-
-export async function updateInterestGroup(id: number, data: CreateInterestGroupArgs): Promise {
- return await prismaCall(() => prisma.interestGroup.update({
- where: {
- id,
- },
+export const update = ServiceMethodHandler({
+ withData: true,
+ validation: updateInterestGroupValidation,
+ handler: async (prisma, { id }: { id: number }, data) => prisma.interestGroup.update({
+ where: { id },
data,
- }))
-}
+ })
+})
diff --git a/src/services/groups/interestGroups/validation.ts b/src/services/groups/interestGroups/validation.ts
new file mode 100644
index 000000000..bac7d4a08
--- /dev/null
+++ b/src/services/groups/interestGroups/validation.ts
@@ -0,0 +1,31 @@
+import { ValidationBase } from '@/services/Validation'
+import { z } from 'zod'
+
+const baseInterestGroupValidation = new ValidationBase({
+ type: {
+ name: z.string(),
+ shortName: z.string(),
+ },
+ details: {
+ name: z.string().min(
+ 3, 'Navn må ha minst 3 tegn'
+ ).max(
+ 30, 'Navn kan ha maks 30 tegn'
+ ).trim(),
+ shortName: z.string().min(
+ 3, 'Kortnavn må ha minst 3 tegn'
+ ).max(
+ 10, 'Kortnavn kan ha maks 10 tegn'
+ ).trim(),
+ },
+})
+
+export const createInterestGroupValidation = baseInterestGroupValidation.createValidation({
+ keys: ['name', 'shortName'],
+ transformer: data => data
+})
+
+export const updateInterestGroupValidation = baseInterestGroupValidation.createValidationPartial({
+ keys: ['name', 'shortName'],
+ transformer: data => data
+})
diff --git a/src/services/groups/memberships/create.ts b/src/services/groups/memberships/create.ts
index 5dea0e8fa..61b43dc44 100644
--- a/src/services/groups/memberships/create.ts
+++ b/src/services/groups/memberships/create.ts
@@ -5,6 +5,7 @@ import { readCurrentOmegaOrder } from '@/services/omegaOrder/read'
import { prismaCall } from '@/services/prismaCall'
import { ServerError } from '@/services/error'
import prisma from '@/prisma'
+import { invalidateManyUserSessionData, invalidateOneUserSessionData } from '@/services/auth/invalidateSession'
import type { ExpandedMembership } from './Types'
export async function createMembershipForUser(
@@ -18,7 +19,7 @@ export async function createMembershipForUser(
}
const order = orderArg ?? await readCurrentGroupOrder(groupId)
- return await prismaCall(() => prisma.membership.create({
+ const membership = await prismaCall(() => prisma.membership.create({
data: {
group: {
connect: {
@@ -39,6 +40,8 @@ export async function createMembershipForUser(
active: true,
},
}))
+ await invalidateOneUserSessionData(userId)
+ return membership
}
/**
@@ -80,6 +83,7 @@ export async function createMembershipsForGroup(
})),
skipDuplicates: true,
}))
+ await invalidateManyUserSessionData(data.map(({ userId }) => userId))
}
/**
@@ -121,4 +125,6 @@ export async function createMembershipsForUser(
})),
skipDuplicates: true,
}))
+
+ await invalidateOneUserSessionData(userId)
}
diff --git a/src/services/groups/memberships/destroy.ts b/src/services/groups/memberships/destroy.ts
index 910346d24..e5439239a 100644
--- a/src/services/groups/memberships/destroy.ts
+++ b/src/services/groups/memberships/destroy.ts
@@ -4,6 +4,7 @@ import { readCurrentGroupOrder } from '@/services/groups/read'
import { prismaCall } from '@/services/prismaCall'
import { ServerError } from '@/services/error'
import prisma from '@/prisma'
+import { invalidateManyUserSessionData, invalidateOneUserSessionData } from '@/services/auth/invalidateSession'
import type { ExpandedMembership } from './Types'
export async function destoryMembershipOfUser({
@@ -20,7 +21,7 @@ export async function destoryMembershipOfUser({
}
const order = orderArg ?? await readCurrentGroupOrder(groupId)
- return await prismaCall(() => prisma.membership.delete({
+ const membership = await prismaCall(() => prisma.membership.delete({
where: {
userId_groupId_order: {
groupId,
@@ -29,6 +30,8 @@ export async function destoryMembershipOfUser({
}
},
}))
+ await invalidateOneUserSessionData(userId)
+ return membership
}
export async function destroyMembershipOfUsers(
@@ -50,4 +53,5 @@ export async function destroyMembershipOfUsers(
order,
},
}))
+ invalidateManyUserSessionData(userIds)
}
diff --git a/src/services/groups/memberships/update.ts b/src/services/groups/memberships/update.ts
index 8b5cd084d..c08523baa 100644
--- a/src/services/groups/memberships/update.ts
+++ b/src/services/groups/memberships/update.ts
@@ -2,6 +2,7 @@ import 'server-only'
import { readCurrentGroupOrder } from '@/services/groups/read'
import { prismaCall } from '@/services/prismaCall'
import prisma from '@/prisma'
+import { invalidateOneUserSessionData } from '@/services/auth/invalidateSession'
import type { ExpandedMembership } from './Types'
export async function updateMembership({
@@ -18,7 +19,7 @@ export async function updateMembership({
}): Promise {
const order = (orderArg && typeof orderArg === 'number') ? orderArg : await readCurrentGroupOrder(groupId)
- return await prismaCall(() => prisma.membership.update({
+ const membership = await prismaCall(() => prisma.membership.update({
where: {
userId_groupId_order: {
groupId,
@@ -28,4 +29,6 @@ export async function updateMembership({
},
data
}))
+ invalidateOneUserSessionData(userId)
+ return membership
}
diff --git a/src/services/permissionRoles/ConfigVars.ts b/src/services/permissionRoles/ConfigVars.ts
index 616675e41..937c13e23 100644
--- a/src/services/permissionRoles/ConfigVars.ts
+++ b/src/services/permissionRoles/ConfigVars.ts
@@ -92,24 +92,14 @@ export const PermissionConfig = {
description: 'kan oppdatere komite',
category: 'groups',
},
- INTEREST_GROUP_CREATE: {
- name: 'Lage interessegruppe',
- description: 'kan lage interessegruppe',
- category: 'groups',
- },
- INTEREST_GROUP_DESTROY: {
- name: 'Slette interessegruppe',
- description: 'kan slette interessegruppe',
- category: 'groups',
- },
INTEREST_GROUP_READ: {
name: 'Les interessegruppe',
description: 'kan lese interessegruppe',
category: 'groups',
},
- INTEREST_GROUP_UPDATE: {
- name: 'Oppdatere interessegruppe',
- description: 'kan oppdatere interessegruppe',
+ INTEREST_GROUP_ADMIN: {
+ name: 'Administrere interessegruppe',
+ description: 'Administrere interessegruppe uten å være admin i gruppen. Og lage nye grupper',
category: 'groups',
},
OMEGA_MEMBERSHIP_GROUP_UPDATE: {