Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ const Header = () => {
</button>
<div className={"dropdown-menu absolute bg-gray-700 text-white rounded-b-lg pb-2 w-51 hidden z-10"}>
<Link onClick={closeDropdown} className="block px-6 py-2 hover:font-semibold" to="/completion">{t('completion_page.title')}</Link>
<Link onClick={closeDropdown} className="block px-6 py-2 hover:font-semibold" to="/completion/new-project">{t('new_completion_project_page.title')}</Link>
<Link onClick={closeDropdown} className="block px-6 py-2 hover:font-semibold" to="/completion/new-project">{t('new_project_completion_page.title')}</Link>
</div>
</div>

Expand Down
66 changes: 63 additions & 3 deletions src/i18n/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,68 @@
"content": "Inhalt",
"downloads": "Hilfsblatt herunterladen"
},
"new_completion_project_page": {
"title": "Fragebogen"
"new_project_completion_page": {
"title": "Fragebogen",
"result": {
"hints": {
"title": "ToDo",
"defaults": {
"contact_itkom": "Kontaktiere die IT-Kommission, damit das Tool in der IT-Landschaft aktualisiert werden kann.",
"fill_concept": "Fülle das Betriebskonzept aus un reiche es ein, eine Vorlage findest du im Downloadbereich."
}
},
"answers": {
"title": "Deine Antworten"
}
},
"questions": {
"next": "weiter",
"landscape": {
"text": "Ist das Tool bereits in der IT-Landaschaft dokumentiert?",
"responses": {
"yes": "Ja",
"no": "Nein"
},
"landscape_no": "Das Tool ist nicht in der IT-Landschaft dokumentiert. Bitte kontaktiere die IT-Kommission, damit das Tool in der IT-Landschaft aufgenommen werden kann"
},
"roles": {
"text": "Welche Rollen sind für das Tool definiert?",
"responses": {
"po": "Product Owner",
"technical_contact": "Technischer Kontakt",
"content_management": "Content Management"
}
},
"artifacts": {
"text": "Welche Artefakte sind für das Tool erstellt worden?",
"responses": {
"documentation": "Dokumentation / Architektur",
"passwords": "Passwörter / Zugriffe",
"code": "Sourcecode",
"api_documentation": "API Dokumentation",
"specifications": "Spezifikationen"
}
},
"budget": {
"text": "Sind die laufenden Kosten für den Betrieb des Tools geklärt?",
"responses": {
"no_costs": "Es fallen keine Kosten an",
"yes_defined": "Ja, es sind alle Kosten definiert",
"not_defined": "Weiss nicht, ob es Kosten gibt"
},
"budget_no_costs": "Keine Kosten? Bitte prüf dies nochmals",
"budget_yes_defined": "Super, die Kosten sind definiert. Bitte stelle sicher, dass diese auch im Budget der PBS berücksichtigt werden.",
"budget_not_defined": "Bitte kläre die Kosten ab, damit die PBS sie für den Betrieb des Tools berücksichtigen kann."
},
"tasks": {
"text": "Welche Aufgaben fallen an, um das Tool zu betreiben und zu unterhalten?",
"responses": {
"none": "Es gibt keine Aufgaben",
"still_open": "Weiss nicht, ob es Aufgaben gibt",
"defined": "Ja, es sind alle Aufgaben definiert"
}
}
}
},
"principles_page": {
"title": "Prinzipien",
Expand Down Expand Up @@ -391,4 +451,4 @@
}
}
}
}
}
64 changes: 62 additions & 2 deletions src/i18n/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,68 @@
"content": "Contenu",
"downloads": "# Hilfsblatt herunterladen"
},
"new_completion_project_page": {
"title": "# Fragebogen"
"new_project_completion_page": {
"title": "Questionnaire",
"result": {
"hints": {
"title": "À faire",
"defaults": {
"contact_itkom": "Contacte la Commission IT pour que l’outil soit mis à jour dans le paysage IT.",
"fill_concept": "Remplis le concept d’exploitation et soumets-le, un modèle se trouve dans la zone de téléchargement."
}
},
"answers": {
"title": "Tes réponses"
}
},
"questions": {
"next": "suivant",
"landscape": {
"text": "L’outil est-il déjà documenté dans le paysage IT ?",
"responses": {
"yes": "Oui",
"no": "Non"
},
"landscape_no": "L’outil n’est pas documenté dans le paysage IT. Merci de contacter la Commission IT pour qu’il soit intégré."
},
"roles": {
"text": "Quelles sont les rôles définis pour l’outil ?",
"responses": {
"po": "Product Owner",
"technical_contact": "Contact technique",
"content_management": "Gestion du contenu"
}
},
"artifacts": {
"text": "Quels artefacts ont été créés pour l’outil ?",
"responses": {
"documentation": "Documentation / Architecture",
"passwords": "Mots de passe / Accès",
"code": "Code source",
"api_documentation": "Documentation API",
"specifications": "Spécifications"
}
},
"budget": {
"text": "Les coûts d’exploitation de l’outil sont-ils clarifiés ?",
"responses": {
"no_costs": "Aucun coût",
"yes_defined": "Oui, tous les coûts sont définis",
"not_defined": "Je ne sais pas s’il y a des coûts"
},
"budget_no_costs": "Aucun coût ? Merci de vérifier à nouveau.",
"budget_yes_defined": "Super, les coûts sont définis. Merci de t’assurer qu’ils sont pris en compte dans le budget du MSdS.",
"budget_not_defined": "Merci de clarifier les coûts afin que le MSdS puisse les prendre en compte pour l’exploitation de l’outil."
},
"tasks": {
"text": "Quelles tâches sont nécessaires pour exploiter et entretenir l’outil ?",
"responses": {
"none": "Aucune tâche",
"still_open": "Je ne sais pas s’il y a des tâches",
"defined": "Oui, toutes les tâches sont définies"
}
}
}
},
"principles_page": {
"title": "Principes",
Expand Down
2 changes: 1 addition & 1 deletion src/pages/CompletionPage.tsx
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hier müsste man die imports noch cleanen @ewangler

Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export default function CompletionPage() {
<h1>{t('completion_page.title')}</h1>
<p>{t('completion_page.introduction')}</p>
<Infobox>
<Link to="/completion/tbd">{t('completion_page.complete_project')}</Link>
<Link to="/completion/new-project">{t('completion_page.complete_project')}</Link>
</Infobox>

<div>
Expand Down
182 changes: 180 additions & 2 deletions src/pages/completion/NewCompletionProjectPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,193 @@
import { useTranslation } from 'react-i18next';
import { Helmet } from 'react-helmet'
import { MainContainer } from '../../App'
import { Checkbox, State } from '../digitalisation/NewDigitalisationProjectPage';
import { useState } from 'react';
import { Question, Response, Root, Reply } from '../../types';
import ClippyStage from '../../components/ClippyStage';
import questionJson from './completion_questions.json';
import { Button, ButtonContainer } from '../HomePage';

const defaultState = (): State => ({ currentSlide: 'projectPhase', clippyVariant: 'focus' })
const defaultReplies = (): Reply[] => ([])

export default function NewCompletionProjectPage() {
const [state] = useState<State>(defaultState)
const [replies, setReplies] = useState<Reply[]>(defaultReplies)
const [currentQuestion, setCurrentQuestion] = useState<number>(0)
const [showResult, setShowResult] = useState<Boolean>(false);

const { t } = useTranslation()

const questionsRoot: Root = questionJson
const questions: Question[] = questionsRoot.questions

function nextQuestion(option: Response) {
let nextQuestion = currentQuestion + 1;
if (option.next_question) {
nextQuestion = option.next_question
}

if (nextQuestion < questionJson.questions.length) {
setCurrentQuestion(nextQuestion);
} else {
setShowResult(true)
}
return
}
function replyWith(option: Response, question: Question, event?: React.ChangeEvent<HTMLInputElement>) {
if (event) {
if (event.target.checked) {

const existing = replies.find((r: Reply) => r.id === question.key + option.key)
if (existing) {
setReplies(replies.filter(a =>
a.id !== question.key + option.key
))
} else {
setReplies([
...replies,
{ id: question.key + option.key, question: question, response: option }
])
}
} else {
setReplies(replies.filter(a =>
a.id !== question.key + option.key
))
}
return
}

nextQuestion(option)

const found = replies.find((r: Reply) => r.question.key === question.key)
if (found) {
found.response = option
const newReplies = replies.map<Reply>((r: Reply) => r.question.key === question.key ? found : r)
setReplies(newReplies)
} else {
setReplies([
...replies,
{ id: `${question.key}-${option.key}`, question: question, response: option }
])
}
}

function printOptions(question: Question) {
if (question.multiple_choice && question.multiple_choice === true) {
const checkboxes = question.responses.map(function (option: Response) {
return <div>
<label>
<Checkbox id={option.key + question.key} type="checkbox" value={option.key} name={question.key}
onChange={(e) => replyWith(option, question, e)} />
{t(`new_project_completion_page.questions.${question.key}.responses.${option.key}`)}
</label>
</div>
})
return <div id={question.key} key={question.key}>
{checkboxes}<br />
<Button type="button" onClick={() => nextQuestion(question.responses[0])}>{t('new_project_completion_page.questions.next')}</Button>
</div>
}

return question.responses.map(function (option: Response) {
return <Button key={option.key} type="button" onClick={() => replyWith(option, question)}>
{t(`new_project_completion_page.questions.${question.key}.responses.${option.key}`)}
</Button>
})
}

function splitReplies(replies: Reply[]) {
// Single-choice replies (not multiple_choice)
const singleChoiceReplies = replies.filter(
(reply) => !reply.question.multiple_choice
);

// Grouped multiple-choice replies
const multiChoiceGroups: { [questionKey: string]: Reply[] } = {};
replies.forEach((reply) => {
if (reply.question.multiple_choice) {
if (!multiChoiceGroups[reply.question.key]) {
multiChoiceGroups[reply.question.key] = [];
}
multiChoiceGroups[reply.question.key].push(reply);
}
});

return {
singleChoiceReplies,
multiChoiceGroups, // object: { [questionKey]: Reply[] }
};
}

function calculateResult() {
let hints: string[] = []
let defaultHints = [
t("new_project_completion_page.result.hints.defaults.contact_itkom"),
t("new_project_completion_page.result.hints.defaults.fill_concept")
]
hints.push(...defaultHints)

replies.map(function (reply: Reply) {
if (reply.response.result_hint_key) {
hints.push(t(`new_project_completion_page.questions.${reply.question.key}.${reply.response.result_hint_key}`))
}
})

const { singleChoiceReplies, multiChoiceGroups } = splitReplies(replies);

return <div>
<h2>{t("new_project_completion_page.result.hints.title")}</h2>
<ul>
{hints.map((hint, index) => <li key={index}>{hint}</li>)}
</ul>

<h2>{t("new_project_completion_page.result.answers.title")}</h2>
{singleChoiceReplies.map(function (reply: Reply) {
return (
<div key={reply.id}>
<p>
<em>{t(`new_project_completion_page.questions.${reply.question.key}.text`)}</em>
<br />
{t(`new_project_completion_page.questions.${reply.question.key}.responses.${reply.response.key}`)}
</p>
</div>
)
})}
{Object.entries(multiChoiceGroups).map(([questionKey, groupReplies]: [string, Reply[]]) => (
<div key={questionKey}>
<p>
<em>{t(`new_project_completion_page.questions.${questionKey}.text`)}</em>
</p>
<ul>
{groupReplies.map((reply) => (
<li key={reply.id}>
{t(`new_project_completion_page.questions.${reply.question.key}.responses.${reply.response.key}`)}
</li>
))}
</ul>
</div>
))}
</div>
}

return <MainContainer>
<Helmet>
<title>{t('new_completion_project_page.title')}</title>
<title>{t('new_project_completion_page.title')}</title>
</Helmet>
<h1>{t('new_completion_project_page.title')}</h1>
<h1>{t('new_project_completion_page.title')}</h1>

<ClippyStage variant={state.clippyVariant}>
{showResult ?
<div>
{calculateResult()}
</div>
:
<div className='question-section'>
<div className='question-text'><p>{t(`new_project_completion_page.questions.${questions[currentQuestion].key}.text`)}</p></div>
<div className='answer-section'><ButtonContainer>{printOptions(questions[currentQuestion])}</ButtonContainer></div>
</div>
}
</ClippyStage>
</MainContainer >
}
Loading