Skip to content

Commit

Permalink
Merge pull request #347 from vevcom/feat/interest-group-pages
Browse files Browse the repository at this point in the history
Feat/Interest Group Pages
  • Loading branch information
theodorklauritzen authored Nov 7, 2024
2 parents 0ef8f2d + e76fc78 commit 749c4ae
Show file tree
Hide file tree
Showing 31 changed files with 448 additions and 85 deletions.
5 changes: 5 additions & 0 deletions src/actions/groups/interestGroups/create.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
'use server'
import { Action } from '@/actions/Action'
import { InterestGroups } from '@/services/groups/interestGroups'

export const createInterestGroupAction = Action(InterestGroups.create)
5 changes: 5 additions & 0 deletions src/actions/groups/interestGroups/destroy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
'use server'
import { ActionNoData } from '@/actions/Action'
import { InterestGroups } from '@/services/groups/interestGroups'

export const destroyInterestGroupAction = ActionNoData(InterestGroups.destroy)
5 changes: 5 additions & 0 deletions src/actions/groups/interestGroups/read.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
'use server'
import { ActionNoData } from '@/actions/Action'
import { InterestGroups } from '@/services/groups/interestGroups'

export const readInterestGroupsAction = ActionNoData(InterestGroups.readAll)
5 changes: 5 additions & 0 deletions src/actions/groups/interestGroups/update.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
'use server'
import { Action } from '@/actions/Action'
import { InterestGroups } from '@/services/groups/interestGroups'

export const updateInterestGroupAction = Action(InterestGroups.update)
2 changes: 1 addition & 1 deletion src/app/_components/NavBar/navDef.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ export const itemsForMenu: NavItem[] = [
},
{
name: 'Intressegrupper',
href: '/infopages/interessegrupper',
href: '/interest-groups',
show: 'all',
icon: faGamepad,
},
Expand Down
19 changes: 19 additions & 0 deletions src/app/interest-groups/CreateInterestGroupForm.module.scss
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
20 changes: 20 additions & 0 deletions src/app/interest-groups/CreateInterestGroupForm.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<div className={styles.CreateInterestGroupForm}>
<h2>Lag interessegruppe</h2>
<Form
refreshOnSuccess
action={createInterestGroupAction.bind(null, {})}
submitText="Lag interessegruppe"
>
<TextInput name="name" label="Navn" />
<TextInput name="shortName" label="Kortnavn" />
</Form>
</div>
)
}
16 changes: 16 additions & 0 deletions src/app/interest-groups/InterestGroup.module.scss
Original file line number Diff line number Diff line change
@@ -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;
}
}
80 changes: 80 additions & 0 deletions src/app/interest-groups/InterestGroup.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<div className={styles.interestGroup}>
<h2>{interestGroup.name}</h2>
<div className={styles.admin}>
{
canUpdate.authorized || canDestroy.authorized ? (
<SettingsHeaderItemPopUp PopUpKey={PopUpKey}>
{
canUpdate.authorized && (
<>
<h2>Update interest group</h2>
<Form
refreshOnSuccess
closePopUpOnSuccess={PopUpKey}
action={updateInterestGroupAction.bind(
null, { id: interestGroup.id }
)}
submitText="Endre"
>
<TextInput
defaultValue={interestGroup.name}
name="name"
label="Navn"
/>
<TextInput
defaultValue={interestGroup.shortName}
name="shortName"
label="Kortnavn"
/>
</Form>
</>
)
}
{
canDestroy.authorized && (
<Form
refreshOnSuccess
closePopUpOnSuccess={PopUpKey}
action={destroyInterestGroupAction.bind(
null, { id: interestGroup.id }
)}
submitText="Slett"
submitColor="red"
confirmation={{
confirm: true,
text: `Er du sikker på at du vil slette ${interestGroup.name}?`
}}
/>
)
}
</SettingsHeaderItemPopUp>
) : <></>
}
</div>
<ArticleSection key={interestGroup.id} articleSection={interestGroup.articleSection} />
</div>
)
}
34 changes: 34 additions & 0 deletions src/app/interest-groups/page.tsx
Original file line number Diff line number Diff line change
@@ -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 <div>Failed to load interest groups</div> //TODO: Change to unwrap
const interestGroups = interestGroupsRes.data

const canCreate = CreateInterestGroupAuther.dynamicFields({}).auth(session)

return (
<PageWrapper title="Interessegrupper" headerItem={
canCreate.authorized && (
<AddHeaderItemPopUp PopUpKey="Create interest group">
<CreateInterestGroupForm />
</AddHeaderItemPopUp>
)
}>
<SpecialCmsParagraph special="INTEREST_GROUP_GENERAL_INFO" />
<main>
{
interestGroups.map(interestGroup => (
<InterestGroup session={session} key={interestGroup.id} interestGroup={interestGroup} />
))
}
</main>
</PageWrapper>
)
}
13 changes: 13 additions & 0 deletions src/auth/auther/RequirePermissionOrGroupAdmin.ts
Original file line number Diff line number Diff line change
@@ -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,
}))
Original file line number Diff line number Diff line change
@@ -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 [email protected] 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 [email protected], så tar vi kontakt.

Få støtte! Ønsker din gruppe å søke støtte, send en søknad til [email protected]. 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.
7 changes: 7 additions & 0 deletions src/prisma/prismaservice/src/development/seedDevGroups.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
4 changes: 4 additions & 0 deletions src/prisma/prismaservice/src/seedCmsConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
6 changes: 4 additions & 2 deletions src/prisma/schema/cms.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ enum SpecialCmsParagraph {
FRONTPAGE_2
FRONTPAGE_3
FRONTPAGE_4
INTEREST_GROUP_GENERAL_INFO
CAREER_INFO
}

Expand Down Expand Up @@ -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
}
Expand Down
5 changes: 4 additions & 1 deletion src/prisma/schema/group.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
4 changes: 1 addition & 3 deletions src/prisma/schema/permission.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
13 changes: 9 additions & 4 deletions src/services/ServiceMethod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
})
Expand All @@ -99,10 +102,12 @@ export function ServiceMethod<
} satisfies ServiceMethod<true, TypeType, DetailedType, Params, Return, WantsToOpenTransaction, false> : {
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(
Expand Down
8 changes: 7 additions & 1 deletion src/services/ServiceTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ export type ServiceMethodHandlerConfig<
) => Promise<Return>,
} : {
withData: false,
wantsToOpenTransaction?: WantsToOpenTransaction,
handler: (
prisma: PrismaPossibleTransaction<WantsToOpenTransaction>,
params: Params,
Expand All @@ -172,8 +173,13 @@ type ServiceMethodHandlerAuthConfig<
DynamicFields extends object,
> = {
auther: AutherStaticFieldsBound<DynamicFields, 'USER_NOT_REQUIERED_FOR_AUTHORIZED' | 'USER_REQUIERED_FOR_AUTHORIZED'>
} & ({
dynamicFields: (dataParams: DynamicFieldsInput<WithValidation, Params, DetailedType>) => DynamicFields
}
dynamicFieldsAsync?: never
} | {
dynamicFieldsAsync: (dataParams: DynamicFieldsInput<WithValidation, Params, DetailedType>) => Promise<DynamicFields>
dynamicFields?: never
})

export type ServiceMethodConfig<
WithValidation extends boolean,
Expand Down
10 changes: 10 additions & 0 deletions src/services/groups/interestGroups/Auther.ts
Original file line number Diff line number Diff line change
@@ -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' })
5 changes: 4 additions & 1 deletion src/services/groups/interestGroups/Types.ts
Original file line number Diff line number Diff line change
@@ -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
}
Loading

0 comments on commit 749c4ae

Please sign in to comment.