diff --git a/packages/hackathon/src/components/ProjectViewDialog/ProjectViewDialog.tsx b/packages/hackathon/src/components/ProjectViewDialog/ProjectViewDialog.tsx index d2f392389..a9b141215 100644 --- a/packages/hackathon/src/components/ProjectViewDialog/ProjectViewDialog.tsx +++ b/packages/hackathon/src/components/ProjectViewDialog/ProjectViewDialog.tsx @@ -23,28 +23,31 @@ SOFTWARE. */ + import type { FC } from 'react' import React from 'react' import { Dialog, DialogHeader, DialogContent } from '@looker/components' -import { ProjectView } from '../../scenes/ProjectViewScene/components' import type { IProjectProps } from '../../models' +import { ProjectView } from '../../scenes' interface ProjectViewDialogProps { + /** if assigned, dialog appears. If undefined, dialog is hidden */ project?: IProjectProps - closer: () => void + /** Dialog closing event handler */ + onClose: () => void } export const ProjectViewDialog: FC = ({ project, - closer, + onClose, }) => { const closeDialog = () => { - closer() + onClose() } return ( - Judging {project?.title} + {project?.title} {!!project && } diff --git a/packages/hackathon/src/routes/AppRouter.tsx b/packages/hackathon/src/routes/AppRouter.tsx index ad5e62932..6f4dab194 100644 --- a/packages/hackathon/src/routes/AppRouter.tsx +++ b/packages/hackathon/src/routes/AppRouter.tsx @@ -38,7 +38,7 @@ import { ProjectEditorScene, ResourceScene, } from '../scenes' -import { ProjectViewScene } from '../scenes/ProjectViewScene' + export enum Routes { HOME = '/home', ADMIN = '/admin', @@ -135,11 +135,6 @@ export const AppRouter: FC = ({ authorizedRoutes, hacker }) => ( )} - {authorizedRoutes.includes(Routes.VIEW_PROJECT) && ( - - - - )} {authorizedRoutes.includes(Routes.USERS) && ( diff --git a/packages/hackathon/src/scenes/JudgingEditorScene/components/JudgingForm.tsx b/packages/hackathon/src/scenes/JudgingEditorScene/components/JudgingForm.tsx index cb8c95fdf..652f5e8bf 100644 --- a/packages/hackathon/src/scenes/JudgingEditorScene/components/JudgingForm.tsx +++ b/packages/hackathon/src/scenes/JudgingEditorScene/components/JudgingForm.tsx @@ -34,9 +34,10 @@ import { ButtonOutline, Space, Span, - Text, SpaceVertical, FieldTextArea, + Tabs2, + Tab2, } from '@looker/components' import { saveJudgingRequest, @@ -44,7 +45,8 @@ import { } from '../../../data/judgings/actions' import { getHackerState } from '../../../data/hack_session/selectors' import type { IJudgingProps } from '../../../models' -import { Routes } from '../../../routes/AppRouter' +import { Routes } from '../../../routes' +import { JudgingView } from './JudgingView' interface JudgingFormProps { judging: IJudgingProps @@ -79,103 +81,101 @@ export const JudgingForm: FC = ({ judging, readonly }) => { const impact = judging.impact return ( - <> - {!hacker.canAdmin && judging.user_id !== hacker.id && ( - You can't judge this - )} - - {judging.$title} - {judging.$description} - - - - Execution: {execution} - - - - - - Ambition: {ambition} - - - - - - Coolness: {coolness} - - - - - - Impact: {impact} - - - - Total Score: {judging.score} - - - - - - - - - - - Return to judging - - - - - - + + + + {!hacker.canAdmin && judging.user_id !== hacker.id && ( + You are not assigned to judge this project + )} + + {judging.$title} + {judging.$description} + + + + Execution: {execution} + + + + + + Ambition: {ambition} + + + + + + Coolness: {coolness} + + + + + + Impact: {impact} + + + + Total Score: {judging.score} + + + + + + + + Return to judging + + + + + + + + + + + ) } diff --git a/packages/hackathon/src/scenes/JudgingEditorScene/components/JudgingView.tsx b/packages/hackathon/src/scenes/JudgingEditorScene/components/JudgingView.tsx new file mode 100644 index 000000000..bd1b7359f --- /dev/null +++ b/packages/hackathon/src/scenes/JudgingEditorScene/components/JudgingView.tsx @@ -0,0 +1,68 @@ +/* + + MIT License + + Copyright (c) 2021 Looker Data Sciences, Inc. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + + */ + +import type { FC } from 'react' +import React from 'react' +import { useSelector } from 'react-redux' +import type { IJudgingProps } from '../../../models' +import { getTechnologies } from '../../../data/hack_session/selectors' +import { ExtMarkdown } from '../../../components' +import { getMembers, techDescriptions } from '../../utils' + +interface JudgingViewProps { + judging: IJudgingProps +} + +export const JudgingView: FC = ({ judging }) => { + const availableTechnologies = useSelector(getTechnologies) + + const tech = techDescriptions(judging.$technologies, availableTechnologies) + const members = getMembers(judging.$members) + const view = `# ${judging.$title} +by ${members} + +${judging.$description} + +| Criteria | Score | +| :-------- | -----: | +| Execution | ${judging.execution} | +| Ambition | ${judging.ambition} | +| Coolness | ${judging.coolness} | +| Impact | ${judging.impact} | +| **Score** | **${judging.score}** | + +## Judge's notes + +${judging.notes} + +**Uses**: ${tech} + +**Project type**: ${judging.$project_type} + +**Contestant**: ${judging.$contestant ? 'Yes' : 'No'} +` + return +} diff --git a/packages/hackathon/src/scenes/ProjectViewScene/index.ts b/packages/hackathon/src/scenes/JudgingEditorScene/components/JudgingViewDialog.tsx similarity index 57% rename from packages/hackathon/src/scenes/ProjectViewScene/index.ts rename to packages/hackathon/src/scenes/JudgingEditorScene/components/JudgingViewDialog.tsx index 7b020817d..3397169bd 100644 --- a/packages/hackathon/src/scenes/ProjectViewScene/index.ts +++ b/packages/hackathon/src/scenes/JudgingEditorScene/components/JudgingViewDialog.tsx @@ -23,4 +23,33 @@ SOFTWARE. */ -export * from './ProjectViewScene' +import type { FC } from 'react' +import React from 'react' +import { Dialog, DialogHeader, DialogContent } from '@looker/components' +import type { IJudgingProps } from '../../../models' +import { JudgingView } from './JudgingView' + +interface JudgingViewDialogProps { + /** If assigned, Dialog appears. If undefined, dialog closes */ + judging?: IJudgingProps + /** Dialog closing event handler */ + onClose: () => void +} + +export const JudgingViewDialog: FC = ({ + judging, + onClose, +}) => { + const closeDialog = () => { + onClose() + } + + return ( + + Judging {judging?.$title} + + {!!judging && } + + + ) +} diff --git a/packages/hackathon/src/scenes/JudgingEditorScene/components/index.ts b/packages/hackathon/src/scenes/JudgingEditorScene/components/index.ts index 09b945688..142855296 100644 --- a/packages/hackathon/src/scenes/JudgingEditorScene/components/index.ts +++ b/packages/hackathon/src/scenes/JudgingEditorScene/components/index.ts @@ -23,4 +23,7 @@ SOFTWARE. */ + export * from './JudgingForm' +export * from './JudgingView' +export * from './JudgingViewDialog' diff --git a/packages/hackathon/src/scenes/JudgingEditorScene/index.ts b/packages/hackathon/src/scenes/JudgingEditorScene/index.ts index b9f30c223..d4ac2af4c 100644 --- a/packages/hackathon/src/scenes/JudgingEditorScene/index.ts +++ b/packages/hackathon/src/scenes/JudgingEditorScene/index.ts @@ -24,3 +24,4 @@ */ export * from './JudgingEditorScene' +export * from './components' diff --git a/packages/hackathon/src/scenes/JudgingScene/components/JudgingList.tsx b/packages/hackathon/src/scenes/JudgingScene/components/JudgingList.tsx index 6ec3bf919..13779bdc4 100644 --- a/packages/hackathon/src/scenes/JudgingScene/components/JudgingList.tsx +++ b/packages/hackathon/src/scenes/JudgingScene/components/JudgingList.tsx @@ -36,11 +36,7 @@ import { TextSnippet } from '@styled-icons/material-outlined/TextSnippet' import { Create } from '@styled-icons/material-outlined/Create' import { useSelector, useDispatch } from 'react-redux' import { useHistory } from 'react-router-dom' -import type { - IHackerProps, - IJudgingProps, - IProjectProps, -} from '../../../models' +import type { IJudgingProps } from '../../../models' import { sheetCell } from '../../../models' import { getHackerState, @@ -56,24 +52,7 @@ import { } from '../../../data/judgings/selectors' import { canDoJudgingAction } from '../../../utils' import { PAGE_SIZE } from '../../../constants' -import { ProjectViewDialog } from '../../../components/ProjectViewDialog' - -const asProject = ( - hacker: IHackerProps, - judging: IJudgingProps -): IProjectProps => { - return { - _id: 'projectviewdialog', - _row: 0, - _user_id: hacker.id, - title: judging.$title, - project_type: judging.$project_type, - technologies: judging.$technologies, - $members: judging.$members, - description: judging.$description, - contestant: judging.$contestant, - } as IProjectProps -} +import { JudgingViewDialog } from '../../JudgingEditorScene' interface JudgingListProps {} @@ -84,8 +63,8 @@ export const JudgingList: FC = () => { const hacker = useSelector(getHackerState) const judgings = useSelector(getJudgingsState) const currentPage = useSelector(getJudgingsPageNumState) - const [currentProject, setCurrentProject] = useState< - IProjectProps | undefined + const [currentJudging, setCurrentJudging] = useState< + IJudgingProps | undefined >(undefined) useEffect(() => { @@ -96,12 +75,12 @@ export const JudgingList: FC = () => { dispatch(updateJudgingsPageNum(pageNum)) } - const viewProject = (judging: IJudgingProps) => { - setCurrentProject(asProject(hacker, judging)) + const viewJudging = (judging: IJudgingProps) => { + setCurrentJudging(judging) } - const hideProject = () => { - setCurrentProject(undefined) + const closeView = () => { + setCurrentJudging(undefined) } const showJudging = (judgingId: string) => { @@ -114,20 +93,20 @@ export const JudgingList: FC = () => { return ( <> } > - View Project - - } - itemRole="link" - > - {canDoJudgingAction(hacker, judging) - ? 'Update Judging' - : 'View Judging'} + View Judging + {canDoJudgingAction(hacker, judging) && ( + } + itemRole="link" + > + Update Judging + + )} ) } @@ -157,7 +136,7 @@ export const JudgingList: FC = () => { pages={totalPages} onChange={updatePage} /> - + ) } diff --git a/packages/hackathon/src/scenes/ProjectEditorScene/ProjectEditorScene.tsx b/packages/hackathon/src/scenes/ProjectEditorScene/ProjectEditorScene.tsx index 8e5710b6f..96869207e 100644 --- a/packages/hackathon/src/scenes/ProjectEditorScene/ProjectEditorScene.tsx +++ b/packages/hackathon/src/scenes/ProjectEditorScene/ProjectEditorScene.tsx @@ -23,6 +23,7 @@ SOFTWARE. */ + import type { FC } from 'react' import React from 'react' import { ProjectForm } from './components' diff --git a/packages/hackathon/src/scenes/ProjectEditorScene/components/ProjectForm.tsx b/packages/hackathon/src/scenes/ProjectEditorScene/components/ProjectForm.tsx index 486a8046f..76f27b9d1 100644 --- a/packages/hackathon/src/scenes/ProjectEditorScene/components/ProjectForm.tsx +++ b/packages/hackathon/src/scenes/ProjectEditorScene/components/ProjectForm.tsx @@ -80,7 +80,7 @@ import { allHackersRequest } from '../../../data/hackers/actions' import { getJudgesState } from '../../../data/hackers/selectors' import { canUpdateProject, canLockProject } from '../../../utils' import { Routes } from '../../../routes' -import { ProjectView } from '../../ProjectViewScene/components' +import { ProjectView } from '../../ProjectsScene/components/ProjectView' interface ProjectFormProps {} diff --git a/packages/hackathon/src/scenes/ProjectViewScene/ProjectViewScene.tsx b/packages/hackathon/src/scenes/ProjectViewScene/ProjectViewScene.tsx deleted file mode 100644 index a27afcb34..000000000 --- a/packages/hackathon/src/scenes/ProjectViewScene/ProjectViewScene.tsx +++ /dev/null @@ -1,99 +0,0 @@ -/* - - MIT License - - Copyright (c) 2021 Looker Data Sciences, Inc. - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. - - */ -import type { FC } from 'react' -import { useDispatch, useSelector } from 'react-redux' -import { useHistory, useRouteMatch } from 'react-router-dom' -import React, { useEffect } from 'react' -import { SpaceVertical, Space, Button } from '@looker/components' -import { isLoadingState } from '../../data/common/selectors' -import { - getProjectLoadedState, - getProjectState, -} from '../../data/projects/selectors' -import { getProjectRequest } from '../../data/projects/actions' -import { Routes } from '../../routes' -import { Loading } from '../../components' -import { actionMessage } from '../../data/common/actions' -import { ProjectView } from './components' - -export const ProjectViewScene: FC = () => { - const dispatch = useDispatch() - const history = useHistory() - const match = useRouteMatch<{ projectId: string }>('/projectview/:projectId') - const isLoading = useSelector(isLoadingState) - const projectId = match?.params?.projectId - const isProjectLoaded = useSelector(getProjectLoadedState) - const project = useSelector(getProjectState) - - const invalidProject = () => - dispatch(actionMessage('Invalid project', 'critical')) - - useEffect(() => { - if (project) { - if (projectId === 'new') { - invalidProject() - } - } else { - if (isProjectLoaded) { - invalidProject() - } - } - }, [dispatch, history, projectId, project, isProjectLoaded]) - - const handleCancel = () => { - history.push(Routes.PROJECTS) - } - - useEffect(() => { - if (projectId && !project) { - if (projectId === 'new') { - invalidProject() - } else { - dispatch(getProjectRequest(projectId === 'new' ? undefined : projectId)) - } - } - }, [dispatch, projectId, project]) - - return ( - - - {project ? ( - - ) : ( - - )} - - - - - - ) -} diff --git a/packages/hackathon/src/scenes/ProjectsScene/components/ProjectList.tsx b/packages/hackathon/src/scenes/ProjectsScene/components/ProjectList.tsx index f192c3071..6e1730a71 100644 --- a/packages/hackathon/src/scenes/ProjectsScene/components/ProjectList.tsx +++ b/packages/hackathon/src/scenes/ProjectsScene/components/ProjectList.tsx @@ -24,7 +24,7 @@ */ import type { FC } from 'react' -import React, { useEffect } from 'react' +import React, { useEffect, useState } from 'react' import { DataTable, DataTableItem, @@ -58,6 +58,7 @@ import { getCurrentProjectsState, getProjectsPageNumState, } from '../../../data/projects/selectors' +import { ProjectViewDialog } from '../../../components/ProjectViewDialog' interface ProjectListProps {} @@ -70,6 +71,9 @@ export const ProjectList: FC = () => { dispatch(currentProjectsRequest()) }, [dispatch]) const projects = useSelector(getCurrentProjectsState) + const [currentProject, setCurrentProject] = useState< + IProjectProps | undefined + >(undefined) const handleDelete = (project: IProjectProps) => { dispatch(deleteProject(project._id)) @@ -98,10 +102,12 @@ export const ProjectList: FC = () => { }) } - const handleView = (projectId: string) => { - setTimeout(() => { - history.push(`/projectview/${projectId}`) - }) + const handleView = (project: IProjectProps) => { + setCurrentProject(project) + } + + const closeView = () => { + setCurrentProject(undefined) } const actions = (project: IProjectProps) => { @@ -120,7 +126,7 @@ export const ProjectList: FC = () => { )} } itemRole="link" > @@ -198,6 +204,7 @@ export const ProjectList: FC = () => { pages={totalPages} onChange={(pageNumber) => dispatch(updateProjectsPageNum(pageNumber))} /> + ) } diff --git a/packages/hackathon/src/scenes/ProjectViewScene/components/ProjectView.tsx b/packages/hackathon/src/scenes/ProjectsScene/components/ProjectView.tsx similarity index 77% rename from packages/hackathon/src/scenes/ProjectViewScene/components/ProjectView.tsx rename to packages/hackathon/src/scenes/ProjectsScene/components/ProjectView.tsx index 80bc7171e..e426faf34 100644 --- a/packages/hackathon/src/scenes/ProjectViewScene/components/ProjectView.tsx +++ b/packages/hackathon/src/scenes/ProjectsScene/components/ProjectView.tsx @@ -27,35 +27,15 @@ import type { FC } from 'react' import React from 'react' import { useSelector } from 'react-redux' -import type { IProjectProps, ITechnologyProps } from '../../../models' +import type { IProjectProps } from '../../../models' import { ExtMarkdown } from '../../../components' import { getTechnologies } from '../../../data/hack_session/selectors' +import { getMembers, techDescriptions } from '../../utils' interface ProjectViewProps { project: IProjectProps } -const techDescriptions = (ids: string[], technologies?: ITechnologyProps[]) => { - try { - return technologies - ?.filter((t) => ids.includes(t._id)) - .map((t) => t.description) - .join(', ') - } catch { - // avoid sheet data errors - return ids.join(', ') - } -} - -const getMembers = (team: string[]) => { - try { - return team.join(', ') || 'Nobody!' - } catch { - // avoid sheet data errors - return 'Error retrieving team members' - } -} - export const ProjectView: FC = ({ project }) => { const availableTechnologies = useSelector(getTechnologies) diff --git a/packages/hackathon/src/scenes/ProjectsScene/components/index.ts b/packages/hackathon/src/scenes/ProjectsScene/components/index.ts index 7513ad5b6..0f79672f6 100644 --- a/packages/hackathon/src/scenes/ProjectsScene/components/index.ts +++ b/packages/hackathon/src/scenes/ProjectsScene/components/index.ts @@ -23,4 +23,6 @@ SOFTWARE. */ + export * from './ProjectList' +export * from './ProjectView' diff --git a/packages/hackathon/src/scenes/ProjectsScene/index.ts b/packages/hackathon/src/scenes/ProjectsScene/index.ts index b56fea79d..01383ca10 100644 --- a/packages/hackathon/src/scenes/ProjectsScene/index.ts +++ b/packages/hackathon/src/scenes/ProjectsScene/index.ts @@ -23,4 +23,6 @@ SOFTWARE. */ + export * from './ProjectsScene' +export * from './components' diff --git a/packages/hackathon/src/scenes/index.ts b/packages/hackathon/src/scenes/index.ts index 8543f3d43..c09bac6ec 100644 --- a/packages/hackathon/src/scenes/index.ts +++ b/packages/hackathon/src/scenes/index.ts @@ -33,3 +33,4 @@ export * from './AdminScene' export * from './NotFoundScene' export * from './ProjectEditorScene' export * from './ResourceScene' +export * from './utils' diff --git a/packages/hackathon/src/scenes/ProjectViewScene/components/index.ts b/packages/hackathon/src/scenes/utils.ts similarity index 67% rename from packages/hackathon/src/scenes/ProjectViewScene/components/index.ts rename to packages/hackathon/src/scenes/utils.ts index 5e2acb461..702c039e0 100644 --- a/packages/hackathon/src/scenes/ProjectViewScene/components/index.ts +++ b/packages/hackathon/src/scenes/utils.ts @@ -24,4 +24,28 @@ */ -export * from './ProjectView' +import type { ITechnologyProps } from '../models' + +export const techDescriptions = ( + ids: string[], + technologies?: ITechnologyProps[] +) => { + try { + return technologies + ?.filter((t) => ids.includes(t._id)) + .map((t) => t.description) + .join(', ') + } catch { + // avoid sheet data errors + return ids.join(', ') + } +} + +export const getMembers = (team: string[]) => { + try { + return team.join(', ') || 'Nobody!' + } catch { + // avoid sheet data errors + return 'Error retrieving team members' + } +}