Skip to content

Commit

Permalink
Merge cf65240 into d73c976
Browse files Browse the repository at this point in the history
  • Loading branch information
sjschlapbach authored Jan 1, 2025
2 parents d73c976 + cf65240 commit 7030ba3
Show file tree
Hide file tree
Showing 55 changed files with 2,655 additions and 67 deletions.
2 changes: 1 addition & 1 deletion apps/auth/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"@klicker-uzh/prisma": "workspace:*",
"@klicker-uzh/shared-components": "workspace:*",
"@next-auth/prisma-adapter": "1.0.7",
"@uzh-bf/design-system": "3.0.0-alpha.36",
"@uzh-bf/design-system": "3.0.0-alpha.37",
"axios": "1.7.7",
"bcryptjs": "2.4.3",
"js-cookie": "3.0.5",
Expand Down
2 changes: 1 addition & 1 deletion apps/backend-docker/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
"@types/passport": "^1.0.16",
"@types/passport-jwt": "^4.0.1",
"@types/ws": "^8.5.12",
"@uzh-bf/design-system": "3.0.0-alpha.36",
"@uzh-bf/design-system": "3.0.0-alpha.37",
"axios": "~1.7.7",
"cross-env": "~7.0.3",
"dotenv": "~16.4.5",
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"@mdx-js/react": "~3.1.0",
"@tailwindcss/typography": "~0.5.15",
"@types/react": "^18.3.11",
"@uzh-bf/design-system": "3.0.0-alpha.36",
"@uzh-bf/design-system": "3.0.0-alpha.37",
"autoprefixer": "~10.4.20",
"cross-env": "~7.0.3",
"docusaurus-plugin-matomo": "~0.0.8",
Expand Down
2 changes: 1 addition & 1 deletion apps/frontend-control/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"@klicker-uzh/prisma": "workspace:*",
"@klicker-uzh/shared-components": "workspace:*",
"@socialgouv/matomo-next": "1.9.1",
"@uzh-bf/design-system": "3.0.0-alpha.36",
"@uzh-bf/design-system": "3.0.0-alpha.37",
"cross-env": "7.0.3",
"dayjs": "1.11.13",
"deepmerge": "4.3.1",
Expand Down
2 changes: 1 addition & 1 deletion apps/frontend-manage/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"@socialgouv/matomo-next": "1.9.1",
"@tanstack/react-table": "8.20.5",
"@uidotdev/usehooks": "2.4.1",
"@uzh-bf/design-system": "3.0.0-alpha.36",
"@uzh-bf/design-system": "3.0.0-alpha.37",
"dayjs": "1.11.13",
"deepmerge": "4.3.1",
"formik": "2.4.6",
Expand Down
7 changes: 7 additions & 0 deletions apps/frontend-manage/src/components/common/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@ function Header({ user }: HeaderProps): React.ReactElement {
active: router.pathname == '/courses',
cy: 'courses',
},
{
new: true,
href: '/resources',
label: t('manage.general.resources'),
active: router.pathname == '/resources',
cy: 'resources',
},
...(user?.featurePreview
? [
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { useQuery } from '@apollo/client'
import { GetAnswerCollectionsDocument } from '@klicker-uzh/graphql/dist/ops'
import { H2 } from '@uzh-bf/design-system'
import { useTranslations } from 'next-intl'
import AnswerCollectionList from './answerCollections/AnswerCollectionList'
import CreateAddCollection from './answerCollections/CreateAddCollection'
import SharedAnswerCollectionList from './SharedAnswerCollectionList'

function AnswerCollections() {
const t = useTranslations()
const { data, loading } = useQuery(GetAnswerCollectionsDocument)

return (
<div className="h-full w-full">
<H2>{t('manage.resources.answerCollections')}</H2>
<div className="mb-2">
{t('manage.resources.answerCollectionsDescription')}
</div>
<CreateAddCollection />
<AnswerCollectionList
collections={data?.getAnswerCollections?.answerCollections}
loading={loading}
/>
<SharedAnswerCollectionList
sharedCollections={data?.getAnswerCollections?.sharedCollections}
requestedCollections={data?.getAnswerCollections?.requestedCollections}
loading={loading}
/>
</div>
)
}

export default AnswerCollections
18 changes: 18 additions & 0 deletions apps/frontend-manage/src/components/resources/MediaLibrary.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { H2, UserNotification } from '@uzh-bf/design-system'
import { useTranslations } from 'next-intl'

function MediaLibrary() {
const t = useTranslations()

return (
<div className="h-full w-full">
<H2>{t('manage.resources.mediaLibrary')}</H2>
<UserNotification
type="info"
message={t('manage.resources.mediaLibraryAvailableSoon')}
/>
</div>
)
}

export default MediaLibrary
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { AnswerCollection } from '@klicker-uzh/graphql/dist/ops'
import { useTranslations } from 'next-intl'
import AnswerCollectionCollapsible from './answerCollections/AnswerCollectionCollapsible'

function SharedAnswerCollectionList({
sharedCollections,
requestedCollections,
loading,
}: {
sharedCollections?: AnswerCollection[]
requestedCollections?: AnswerCollection[]
loading: boolean
}) {
const t = useTranslations()

// TODO: add shared answer collection list

return (
<AnswerCollectionCollapsible
title={t('manage.resources.sharedAnswerCollections')}
>
LIST
</AnswerCollectionCollapsible>
)
}

export default SharedAnswerCollectionList
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import { useMutation } from '@apollo/client'
import { faSave } from '@fortawesome/free-regular-svg-icons'
import { faPlusCircle } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import {
AddAnswerCollectionOptionDocument,
GetAnswerCollectionsDocument,
} from '@klicker-uzh/graphql/dist/ops'
import { Button, FormikTextField } from '@uzh-bf/design-system'
import { Form, Formik } from 'formik'
import { useTranslations } from 'next-intl'
import { Dispatch, SetStateAction, useState } from 'react'
import * as Yup from 'yup'

function AddAnswerCollectionEntry({
collectionId,
setOptionsEditingDisabled,
}: {
collectionId: number
setOptionsEditingDisabled: Dispatch<SetStateAction<boolean>>
}) {
const t = useTranslations()
const [fieldOpen, setFieldOpen] = useState(false)
const [addAnswerCollectionOption] = useMutation(
AddAnswerCollectionOptionDocument
)

if (!fieldOpen) {
return (
<Button
className={{ root: 'w-full' }}
onClick={() => {
setFieldOpen(true)
setOptionsEditingDisabled(true)
}}
>
<FontAwesomeIcon icon={faPlusCircle} className="mr-1" />
{t('manage.resources.addAnswerOption')}
</Button>
)
}

return (
<Formik
initialValues={{
newValue: undefined,
}}
validationSchema={Yup.object({
newValue: Yup.string().required(t('manage.resources.valueRequired')),
})}
onSubmit={async (values) => {
await addAnswerCollectionOption({
variables: {
collectionId,
value: values.newValue!,
},
update: (cache, { data }) => {
if (!data?.addAnswerCollectionOption) return

const queryData = cache.readQuery({
query: GetAnswerCollectionsDocument,
})
const previousCollections =
queryData?.getAnswerCollections?.answerCollections
if (!previousCollections) return

cache.writeQuery({
query: GetAnswerCollectionsDocument,
data: {
getAnswerCollections: {
requestedCollections:
queryData.getAnswerCollections?.requestedCollections ?? [],
sharedCollections:
queryData.getAnswerCollections?.sharedCollections ?? [],
answerCollections: previousCollections.map((collection) => {
if (collection.id === collectionId) {
return {
...collection,
entries: [
...(collection.entries ?? []),
data.addAnswerCollectionOption!,
],
}
}

return collection
}),
},
},
})
},
})
setFieldOpen(false)
setOptionsEditingDisabled(false)
}}
validateOnMount
>
{({ values, isValid, isSubmitting }) => (
<Form className="flex flex-row gap-1">
<FormikTextField name="newValue" className={{ input: 'h-8' }} />
<Button
type="submit"
className={{ root: 'border-primary-80 h-8' }}
disabled={!isValid}
loading={isSubmitting}
>
<FontAwesomeIcon icon={faSave} className="mr-0.5" />
<div className="w-max">{t('shared.generic.save')}</div>
</Button>
</Form>
)}
</Formik>
)
}

export default AddAnswerCollectionEntry
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import {
faLock,
faLockOpen,
faUserLock,
} from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { CollectionAccess } from '@klicker-uzh/graphql/dist/ops'
import { FormikSelectField } from '@uzh-bf/design-system'
import { useTranslations } from 'next-intl'

function AnswerCollectionAccessSelection({
restrictedDisabled = false,
privateDisabled = false,
}: {
restrictedDisabled?: boolean
privateDisabled?: boolean
}) {
const t = useTranslations()

return (
<FormikSelectField
required
name="access"
label={t('manage.resources.access')}
tooltip={t('manage.resources.accessTooltip')}
items={[
{
value: CollectionAccess.Private,
label: (
<div className="flex flex-row items-center gap-2 text-red-700">
<FontAwesomeIcon icon={faLock} />
{t(`manage.resources.access${CollectionAccess.Private}`)}
</div>
),
disabled: privateDisabled,
data: { cy: 'answer-collection-access-private' },
},
{
value: CollectionAccess.Public,
label: (
<div className="flex flex-row items-center gap-2 text-green-700">
<FontAwesomeIcon icon={faLockOpen} />
{t(`manage.resources.access${CollectionAccess.Public}`)}
</div>
),
data: { cy: 'answer-collection-access-public' },
},
{
value: CollectionAccess.Restricted,
label: (
<div className="flex flex-row items-center gap-2 text-orange-600">
<FontAwesomeIcon icon={faUserLock} />
{t(`manage.resources.access${CollectionAccess.Restricted}`)}
</div>
),
disabled: restrictedDisabled,
data: { cy: 'answer-collection-access-restricted' },
},
]}
data={{ cy: 'answer-collection-access' }}
className={{ select: { trigger: 'h-9 w-40' } }}
/>
)
}

export default AnswerCollectionAccessSelection
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { faChevronDown, faChevronUp } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { Button, H3 } from '@uzh-bf/design-system'
import { useState } from 'react'

function AnswerCollectionCollapsible({
title,
children,
}: {
title: string
children: React.ReactNode
}) {
const [open, setOpen] = useState(true)

return (
<div className="mb-4">
<Button
basic
onClick={() => setOpen((prev) => !prev)}
className={{
root: 'mb-0 flex w-full flex-row items-center justify-between border-b',
}}
>
<H3>{title}</H3>
<FontAwesomeIcon
icon={open ? faChevronUp : faChevronDown}
className="mb-1 mr-1"
/>
</Button>
{open ? children : null}
</div>
)
}

export default AnswerCollectionCollapsible
Loading

0 comments on commit 7030ba3

Please sign in to comment.