mirror of
https://github.com/UniversityOfHelsinkiCS/oodikone.git
synced 2026-10-12 19:03:39 +03:00
[Study programme] Rewrite Tags tab with MUI
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
import { Add as AddIcon } from '@mui/icons-material'
|
||||
import { Button, FormControlLabel, Stack, Switch, TextField } from '@mui/material'
|
||||
import { DatePicker } from '@mui/x-date-pickers'
|
||||
import { Moment } from 'moment'
|
||||
import { useState } from 'react'
|
||||
|
||||
import { studyProgrammeToolTips } from '@/common/InfoToolTips'
|
||||
import { Section } from '@/components/material/Section'
|
||||
import { YEAR_DATE_FORMAT } from '@/constants/date'
|
||||
import { useGetAuthorizedUserQuery } from '@/redux/auth'
|
||||
import { useCreateTagMutation } from '@/redux/tags'
|
||||
import { Tag } from '@/shared/types'
|
||||
import { reformatDate } from '@/util/timeAndDate'
|
||||
|
||||
export const CreateNewTagSection = ({ studyTrack, tags }: { studyTrack: string; tags: Tag[] }) => {
|
||||
const [tagName, setTagName] = useState<string>('')
|
||||
const [year, setYear] = useState<Moment | null>(null)
|
||||
const [personal, setPersonal] = useState(false)
|
||||
const { id: userId } = useGetAuthorizedUserQuery()
|
||||
const [createTag] = useCreateTagMutation()
|
||||
|
||||
const handleSubmit = event => {
|
||||
event.preventDefault()
|
||||
const newTag = {
|
||||
name: tagName.trim(),
|
||||
personalUserId: personal ? userId : null,
|
||||
studyTrack,
|
||||
year: year ? reformatDate(year.toDate(), YEAR_DATE_FORMAT) : null,
|
||||
}
|
||||
void createTag(newTag)
|
||||
setTagName('')
|
||||
setYear(null)
|
||||
setPersonal(false)
|
||||
}
|
||||
|
||||
const handleChange = ({ target }) => {
|
||||
setTagName(target.value)
|
||||
}
|
||||
|
||||
return (
|
||||
<Section cypress="create-new-tag" infoBoxContent={studyProgrammeToolTips.tags} title="Create new tag">
|
||||
<Stack direction="column">
|
||||
<Stack alignItems="center" direction="row" gap={1}>
|
||||
<TextField
|
||||
label="Tag name"
|
||||
onChange={handleChange}
|
||||
placeholder="Enter a name for the new tag"
|
||||
sx={{ width: 320 }}
|
||||
value={tagName}
|
||||
/>
|
||||
<DatePicker
|
||||
label="Associated start year (optional)"
|
||||
onChange={newYear => setYear(newYear)}
|
||||
sx={{
|
||||
width: 320,
|
||||
}}
|
||||
value={year}
|
||||
views={['year']}
|
||||
/>
|
||||
<Button
|
||||
disabled={!tagName.trim() || tags.some(tag => tag.name === tagName.trim())}
|
||||
endIcon={<AddIcon />}
|
||||
onClick={handleSubmit}
|
||||
variant="contained"
|
||||
>
|
||||
Create
|
||||
</Button>
|
||||
</Stack>
|
||||
<FormControlLabel
|
||||
control={<Switch checked={personal} onChange={() => setPersonal(!personal)} />}
|
||||
label="Personal tag"
|
||||
/>
|
||||
</Stack>
|
||||
</Section>
|
||||
)
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Button, Dropdown, Form, Message, Modal, TextArea } from 'semantic-ui-react'
|
||||
|
||||
import { extractItems } from '@/common'
|
||||
import { useCreateStudentTagsMutation } from '@/redux/tags'
|
||||
|
||||
export const TagModal = ({ combinedProgramme, studytrack, tags }) => {
|
||||
const [modalOpen, setModalOpen] = useState(false)
|
||||
const [input, setInput] = useState('')
|
||||
const [selectedValue, setSelected] = useState('')
|
||||
const [createStudentTags, { isError, isLoading, isSuccess }] = useCreateStudentTagsMutation()
|
||||
const parsedStudentNumbers = extractItems(input)
|
||||
|
||||
useEffect(() => {
|
||||
if (!isLoading) {
|
||||
if (isSuccess) {
|
||||
setSelected('')
|
||||
setInput('')
|
||||
setTimeout(() => setModalOpen(false), 2000)
|
||||
}
|
||||
}
|
||||
}, [isLoading])
|
||||
|
||||
const handleClick = async event => {
|
||||
event.preventDefault()
|
||||
await createStudentTags({
|
||||
tags: parsedStudentNumbers.map(studentNumber => ({
|
||||
tag_id: selectedValue,
|
||||
studentnumber: studentNumber,
|
||||
})),
|
||||
studytrack,
|
||||
combinedProgramme,
|
||||
})
|
||||
}
|
||||
|
||||
const handleChange = (event, { value }) => {
|
||||
event.preventDefault()
|
||||
setSelected(value)
|
||||
}
|
||||
|
||||
const createdOptions = tags.map(tag => ({ key: tag.tag_id, text: tag.tagname, value: tag.tag_id }))
|
||||
|
||||
return (
|
||||
<Modal
|
||||
closeOnEscape={false}
|
||||
onClose={() => setModalOpen(false)}
|
||||
open={modalOpen}
|
||||
size="small"
|
||||
trigger={
|
||||
<Button
|
||||
color="blue"
|
||||
content="Add a tag to students"
|
||||
disabled={!tags.length}
|
||||
onClick={() => setModalOpen(true)}
|
||||
size="small"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Modal.Content>
|
||||
<Form>
|
||||
<h2>Add a tag to students</h2>
|
||||
<Message content="Failed to add tags to students." hidden={!isError} negative />
|
||||
<Message content="Successfully added tags to students." hidden={!isSuccess} positive />
|
||||
<Form.Field>
|
||||
<label>Select a tag to add</label>
|
||||
<Dropdown
|
||||
onChange={handleChange}
|
||||
options={createdOptions}
|
||||
search
|
||||
selectOnBlur={false}
|
||||
selectOnNavigation={false}
|
||||
selection
|
||||
value={selectedValue}
|
||||
/>
|
||||
</Form.Field>
|
||||
<Form.Field>
|
||||
<label>
|
||||
Insert student numbers you wish to tag. Separate each number with a comma, semicolon, space, or newline.
|
||||
</label>
|
||||
<TextArea onChange={(_, { value }) => setInput(value)} placeholder="011111111" rows={10} value={input} />
|
||||
</Form.Field>
|
||||
</Form>
|
||||
</Modal.Content>
|
||||
<Modal.Actions>
|
||||
<Button content="Cancel" negative onClick={() => setModalOpen(false)} />
|
||||
<Button
|
||||
content="Add tags"
|
||||
disabled={isLoading || selectedValue.length === 0 || !parsedStudentNumbers.length}
|
||||
onClick={event => handleClick(event)}
|
||||
positive
|
||||
/>
|
||||
</Modal.Actions>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
import {
|
||||
Button,
|
||||
Dialog,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogContentText,
|
||||
DialogTitle,
|
||||
Stack,
|
||||
TextField,
|
||||
} from '@mui/material'
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
import { extractItems } from '@/common'
|
||||
import { useCreateStudentTagsMutation } from '@/redux/tags'
|
||||
import { Tag } from '@/shared/types'
|
||||
|
||||
export const AddStudentsDialog = ({
|
||||
combinedProgramme,
|
||||
open,
|
||||
setOpen,
|
||||
studyTrack,
|
||||
tag,
|
||||
}: {
|
||||
combinedProgramme: string
|
||||
open: boolean
|
||||
setOpen: (open: boolean) => void
|
||||
studyTrack: string
|
||||
tag: Tag
|
||||
}) => {
|
||||
const [input, setInput] = useState('')
|
||||
const parsedStudentNumbers = extractItems(input)
|
||||
const [createStudentTags, { isError, isLoading, isSuccess }] = useCreateStudentTagsMutation()
|
||||
|
||||
useEffect(() => {
|
||||
if (!isLoading) {
|
||||
if (isSuccess) {
|
||||
setInput('')
|
||||
setTimeout(() => setOpen(false), 2000)
|
||||
}
|
||||
}
|
||||
}, [isLoading, isSuccess, setOpen])
|
||||
|
||||
const handleClick = async event => {
|
||||
event.preventDefault()
|
||||
await createStudentTags({
|
||||
combinedProgramme,
|
||||
studentTags: parsedStudentNumbers.map(studentNumber => ({
|
||||
studentNumber,
|
||||
tagId: tag.id,
|
||||
})),
|
||||
studyTrack,
|
||||
})
|
||||
setInput('')
|
||||
setOpen(false)
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog open={open}>
|
||||
<DialogTitle>Add students to tag {tag.name}</DialogTitle>
|
||||
<DialogContent>
|
||||
<Stack gap={1}>
|
||||
<DialogContentText>Insert student numbers you wish to tag</DialogContentText>
|
||||
<TextField
|
||||
minRows={3}
|
||||
multiline
|
||||
onChange={event => setInput(event.target.value)}
|
||||
placeholder="Separate each student number with a comma, semicolon, space, or newline"
|
||||
sx={{ width: 400 }}
|
||||
value={input}
|
||||
/>
|
||||
</Stack>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={() => setOpen(false)} variant="outlined">
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
color="success"
|
||||
disabled={isError || isLoading || !parsedStudentNumbers.length}
|
||||
onClick={handleClick}
|
||||
variant="contained"
|
||||
>
|
||||
Confirm
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
import { Button, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle } from '@mui/material'
|
||||
|
||||
import { useDeleteTagMutation } from '@/redux/tags'
|
||||
import { Tag } from '@/shared/types'
|
||||
|
||||
export const DeleteTagDialog = ({
|
||||
setTagToDelete,
|
||||
tag,
|
||||
tagToDelete,
|
||||
}: {
|
||||
setTagToDelete: (value: Tag | null) => void
|
||||
tag: Tag
|
||||
tagToDelete: Tag | null
|
||||
}) => {
|
||||
const [deleteTag] = useDeleteTagMutation()
|
||||
|
||||
const handleDeleteTag = (event, tag: Tag) => {
|
||||
event.preventDefault()
|
||||
void deleteTag(tag)
|
||||
setTagToDelete(null)
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog open={tagToDelete?.id === tag.id}>
|
||||
<DialogTitle>Delete tag</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText>
|
||||
Are you sure you want to delete tag <b>{tag.name}</b>? If you press confirm you will delete it from all
|
||||
students that have it. You and other users won't be able to use this tag again.
|
||||
</DialogContentText>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={() => setTagToDelete(null)} variant="outlined">
|
||||
Cancel
|
||||
</Button>
|
||||
<Button color="error" onClick={event => handleDeleteTag(event, tag)} variant="contained">
|
||||
Confirm
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
import {
|
||||
Delete as DeleteIcon,
|
||||
PersonAddAlt1 as PersonAddAlt1Icon,
|
||||
Sell as SellIcon,
|
||||
Visibility as VisibilityIcon,
|
||||
} from '@mui/icons-material'
|
||||
import { IconButton, ListItem, ListItemIcon, ListItemText, Stack, Tooltip } from '@mui/material'
|
||||
import { useState } from 'react'
|
||||
|
||||
import { PopulationLinkWithTag } from '@/components/material/PopulationLink'
|
||||
import { Tag } from '@/shared/types'
|
||||
import { AddStudentsDialog } from './AddStudentsDialog'
|
||||
import { DeleteTagDialog } from './DeleteTagDialog'
|
||||
|
||||
export const TagItem = ({
|
||||
combinedProgramme,
|
||||
studyProgramme,
|
||||
studyTrack,
|
||||
tag,
|
||||
}: {
|
||||
combinedProgramme: string
|
||||
studyProgramme: string
|
||||
studyTrack: string
|
||||
tag: Tag
|
||||
}) => {
|
||||
const [studentsToAddDialogOpen, setStudentsToAddDialogOpen] = useState<boolean>(false)
|
||||
const [tagToDelete, setTagToDelete] = useState<Tag | null>(null)
|
||||
|
||||
return (
|
||||
<>
|
||||
<ListItem
|
||||
secondaryAction={
|
||||
<Stack direction="row">
|
||||
<Tooltip arrow placement="top" title="Add students to tag">
|
||||
<IconButton onClick={() => setStudentsToAddDialogOpen(true)}>
|
||||
<PersonAddAlt1Icon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<Tooltip arrow placement="top" title="Delete tag">
|
||||
<IconButton onClick={() => setTagToDelete(tag)}>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</Stack>
|
||||
}
|
||||
>
|
||||
<ListItemIcon>
|
||||
<SellIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
primary={
|
||||
<Stack direction="row" sx={{ alignItems: 'center' }}>
|
||||
{tag.name}
|
||||
{tag.year && (
|
||||
<PopulationLinkWithTag
|
||||
combinedProgramme={combinedProgramme}
|
||||
studyProgramme={studyProgramme}
|
||||
tag={tag}
|
||||
/>
|
||||
)}
|
||||
{tag.personalUserId && (
|
||||
<Tooltip arrow placement="right" title="Only you can see this tag">
|
||||
<VisibilityIcon fontSize="small" />
|
||||
</Tooltip>
|
||||
)}
|
||||
</Stack>
|
||||
}
|
||||
secondary={tag.year ? `Associated start year ${tag.year}` : 'No associated start year'}
|
||||
/>
|
||||
</ListItem>
|
||||
<AddStudentsDialog
|
||||
combinedProgramme={combinedProgramme}
|
||||
open={studentsToAddDialogOpen}
|
||||
setOpen={setStudentsToAddDialogOpen}
|
||||
studyTrack={studyTrack}
|
||||
tag={tag}
|
||||
/>
|
||||
<DeleteTagDialog setTagToDelete={setTagToDelete} tag={tag} tagToDelete={tagToDelete} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import { List, Stack, Typography } from '@mui/material'
|
||||
|
||||
import { Section } from '@/components/material/Section'
|
||||
import { Tag } from '@/shared/types'
|
||||
import { TagItem } from './TagItem'
|
||||
|
||||
export const TagsSection = ({
|
||||
combinedProgramme,
|
||||
studyProgramme,
|
||||
studyTrack,
|
||||
tags,
|
||||
}: {
|
||||
combinedProgramme: string
|
||||
studyProgramme: string
|
||||
studyTrack: string
|
||||
tags: Tag[]
|
||||
}) => {
|
||||
return (
|
||||
<Section title="Tags">
|
||||
{tags.length === 0 ? (
|
||||
<Typography color="text.secondary">This study programme does not have any tags yet</Typography>
|
||||
) : (
|
||||
<Stack gap={1}>
|
||||
<Typography>Tags associated with this study programme</Typography>
|
||||
<List>
|
||||
{tags.map(tag => (
|
||||
<TagItem
|
||||
combinedProgramme={combinedProgramme}
|
||||
key={tag.id}
|
||||
studyProgramme={studyProgramme}
|
||||
studyTrack={studyTrack}
|
||||
tag={tag}
|
||||
/>
|
||||
))}
|
||||
</List>
|
||||
</Stack>
|
||||
)}
|
||||
</Section>
|
||||
)
|
||||
}
|
||||
@@ -1,180 +1,32 @@
|
||||
import moment from 'moment'
|
||||
import { useState } from 'react'
|
||||
import Datetime from 'react-datetime'
|
||||
import { Link } from 'react-router'
|
||||
import { Button, Confirm, Form, Header, Icon, Item, List, Message, Popup, Segment } from 'semantic-ui-react'
|
||||
import { Stack } from '@mui/material'
|
||||
|
||||
import { SortableTable } from '@/components/SortableTable'
|
||||
import { YEAR_DATE_FORMAT } from '@/constants/date'
|
||||
import { TagModal } from '@/pages/StudyProgramme/TagsTab/TagModal'
|
||||
import { useGetAuthorizedUserQuery } from '@/redux/auth'
|
||||
import { useCreateTagMutation, useGetTagsByStudyTrackQuery, useDeleteTagMutation } from '@/redux/tags'
|
||||
import { reformatDate } from '@/util/timeAndDate'
|
||||
import { useGetTagsByStudyTrackQuery } from '@/redux/tags'
|
||||
import { CreateNewTagSection } from './CreateNewTagSection'
|
||||
import { TagsSection } from './TagsSection'
|
||||
|
||||
export const TagsTab = ({ studyprogramme, combinedProgramme }) => {
|
||||
const [tagname, setTagname] = useState('')
|
||||
const [confirm, setConfirm] = useState(null)
|
||||
const [year, setYear] = useState(null)
|
||||
const [personal, setPersonal] = useState(false)
|
||||
const { id: userId } = useGetAuthorizedUserQuery()
|
||||
const studytrack = combinedProgramme ? `${studyprogramme}-${combinedProgramme}` : studyprogramme
|
||||
const { data: tags } = useGetTagsByStudyTrackQuery(studytrack)
|
||||
const [createTag] = useCreateTagMutation()
|
||||
const [deleteTag] = useDeleteTagMutation()
|
||||
export const TagsTab = ({
|
||||
combinedProgramme,
|
||||
studyProgramme,
|
||||
}: {
|
||||
combinedProgramme: string
|
||||
studyProgramme: string
|
||||
}) => {
|
||||
const studyTrack = combinedProgramme ? `${studyProgramme}-${combinedProgramme}` : studyProgramme
|
||||
const { data: tags } = useGetTagsByStudyTrackQuery(studyTrack)
|
||||
|
||||
if (!tags) return null
|
||||
|
||||
const handleDeleteTag = (event, tag) => {
|
||||
event.preventDefault()
|
||||
deleteTag(tag)
|
||||
setConfirm(null)
|
||||
if (!tags) {
|
||||
return null
|
||||
}
|
||||
|
||||
const handleTagYearSelect = momentYear => {
|
||||
setYear(momentYear)
|
||||
}
|
||||
|
||||
const handleSubmit = event => {
|
||||
event.preventDefault()
|
||||
const newTag = {
|
||||
tagname: tagname.trim(),
|
||||
studytrack: combinedProgramme ? `${studyprogramme}-${combinedProgramme}` : studyprogramme,
|
||||
year: year ? reformatDate(year, YEAR_DATE_FORMAT) : null,
|
||||
personal_user_id: personal ? userId : null,
|
||||
}
|
||||
createTag(newTag)
|
||||
setTagname('')
|
||||
setYear(null)
|
||||
setPersonal(false)
|
||||
}
|
||||
|
||||
const handleChange = ({ target }) => {
|
||||
setTagname(target.value)
|
||||
}
|
||||
|
||||
const deleteButton = tag => <Button content="Delete" negative onClick={() => setConfirm(tag)} />
|
||||
|
||||
const populationUrl = tag => {
|
||||
if (!tag.year) {
|
||||
const year = new Date().getFullYear()
|
||||
const months = Math.ceil(moment.duration(moment().diff(`${year}-08-01`)).asMonths())
|
||||
const href = combinedProgramme
|
||||
? `/populations?months=${months}&semesters=FALL&semesters=` +
|
||||
`SPRING&studyRights=%7B"programme"%3A"${studyprogramme}"%2C"combinedProgramme"%3A"${combinedProgramme}"%7D&year=${year}&tag=${tag.tag_id}`
|
||||
: `/populations?months=${months}&semesters=FALL&semesters=` +
|
||||
`SPRING&studyRights=%7B"programme"%3A"${studyprogramme}"%7D&year=${year}&tag=${tag.tag_id}`
|
||||
return href
|
||||
}
|
||||
const months = Math.ceil(moment.duration(moment().diff(`${tag.year}-08-01`)).asMonths())
|
||||
const href = combinedProgramme
|
||||
? `/populations?months=${months}&semesters=FALL&semesters=` +
|
||||
`SPRING&studyRights=%7B"programme"%3A"${studyprogramme}"%2C"combinedProgramme"%3A"${combinedProgramme}"%7D&year=${tag.year}&tag=${tag.tag_id}`
|
||||
: `/populations?months=${months}&semesters=FALL&semesters=` +
|
||||
`SPRING&studyRights=%7B"programme"%3A"${studyprogramme}"%7D&year=${tag.year}&tag=${tag.tag_id}`
|
||||
return href
|
||||
}
|
||||
|
||||
const decorateTagName = tag => {
|
||||
if (tag.personal_user_id)
|
||||
return (
|
||||
<>
|
||||
{tag.tagname}
|
||||
<Item as={Link} to={populationUrl(tag)}>
|
||||
<Icon name="level up alternate" />
|
||||
</Item>
|
||||
<Popup
|
||||
content="Only you can see this tag."
|
||||
trigger={<Icon color="purple" name="eye" style={{ marginLeft: '1em' }} />}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
return (
|
||||
<>
|
||||
{tag.tagname}
|
||||
<Item as={Link} to={populationUrl(tag)}>
|
||||
<Icon name="level up alternate" />
|
||||
</Item>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const columns = [
|
||||
{
|
||||
key: 'name',
|
||||
title: 'Name',
|
||||
getRowVal: tag => tag.tagname,
|
||||
getRowContent: tag => decorateTagName(tag),
|
||||
},
|
||||
{
|
||||
key: 'year',
|
||||
title: 'Associated start year',
|
||||
getRowVal: tag => tag.year,
|
||||
},
|
||||
{
|
||||
key: 'delete',
|
||||
title: 'Delete',
|
||||
export: false,
|
||||
getRowContent: tag => (
|
||||
<Form.Field>
|
||||
{deleteButton(tag.tag_id)}
|
||||
<Confirm
|
||||
cancelButton="Cancel"
|
||||
confirmButton="Confirm"
|
||||
content={`Are you sure you want to delete tag "${tag.tagname}"? If you press confirm you will delete it from all students that have it. You and other users won't be able to use this tag again.`}
|
||||
onCancel={() => setConfirm(null)}
|
||||
onConfirm={event => handleDeleteTag(event, tag)}
|
||||
open={confirm === tag.tag_id}
|
||||
/>
|
||||
</Form.Field>
|
||||
),
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<List>
|
||||
<Form>
|
||||
<Message
|
||||
content="Here you can create tags for study programme. You can either create public tags or personal tags.
|
||||
Tags can be used to combine students from other starting years. 'Associated start year' means what year you want to use as a start year for the students in that tag.
|
||||
For example with this you can move student from earlier starting year to next year if the student was absent during first year. However this is optional and you can
|
||||
create a new tag without selecting year."
|
||||
header="Create tags for study programme"
|
||||
/>
|
||||
<Segment>
|
||||
<Form.Group>
|
||||
<Form.Field>
|
||||
<label>Tag name</label>
|
||||
<Form.Input className="tagNameSelectInput" onChange={handleChange} value={tagname} />
|
||||
</Form.Field>
|
||||
<Form.Field>
|
||||
<label>Associated start year</label>
|
||||
<Datetime
|
||||
className="yearSelectInput"
|
||||
closeOnSelect
|
||||
control={Datetime}
|
||||
dateFormat={YEAR_DATE_FORMAT}
|
||||
onChange={handleTagYearSelect}
|
||||
renderYear={(props, selectableYear) => <td {...props}>{selectableYear}</td>}
|
||||
timeFormat={false}
|
||||
value={year}
|
||||
/>
|
||||
</Form.Field>
|
||||
<Form.Field>
|
||||
<label>Personal tag</label>
|
||||
<Form.Checkbox checked={personal} onClick={() => setPersonal(!personal)} toggle />
|
||||
</Form.Field>
|
||||
<Button
|
||||
color="green"
|
||||
content="Create a new tag"
|
||||
disabled={!tagname.trim() || tags.some(tag => tag.tagname === tagname.trim())}
|
||||
onClick={handleSubmit}
|
||||
/>
|
||||
<TagModal combinedProgramme={combinedProgramme} studytrack={studyprogramme} tags={tags} />
|
||||
</Form.Group>
|
||||
</Segment>
|
||||
</Form>
|
||||
<Header size="medium">Study programme tags</Header>
|
||||
<SortableTable columns={columns} data={tags} />
|
||||
</List>
|
||||
<Stack gap={2}>
|
||||
<CreateNewTagSection studyTrack={studyTrack} tags={tags} />
|
||||
<TagsSection
|
||||
combinedProgramme={combinedProgramme}
|
||||
studyProgramme={studyProgramme}
|
||||
studyTrack={studyTrack}
|
||||
tags={tags}
|
||||
/>
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ export const StudyProgramme = () => {
|
||||
key: 'TagsTab',
|
||||
cypress: 'tags-tab',
|
||||
label: 'Tags',
|
||||
component: <TagsTab combinedProgramme={secondProgrammeId} studyprogramme={programmeId} />,
|
||||
component: <TagsTab combinedProgramme={secondProgrammeId} studyProgramme={programmeId} />,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user