diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..0160920 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,4 @@ +node_modules/ +.next/ +stories/ +**/*.stories.* diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..72fa056 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,56 @@ +module.exports = { + root: true, + parser: '@typescript-eslint/parser', + plugins: [ + '@typescript-eslint', + ], + extends: [ + 'next/core-web-vitals', + 'airbnb', + 'airbnb-typescript', + 'airbnb/hooks', + 'plugin:@typescript-eslint/recommended', + 'plugin:@typescript-eslint/recommended-requiring-type-checking', + ], + parserOptions: { + project: './tsconfig.json', + }, + rules: { + // Clarifications are provided for each suppression + + /** + * Forcing the use of default export affects readability and + * comprehensibility, especially in locally created package/module code - + * exporting at the discretion of the author is generally much + * more readable and appropriate in many cases + */ + 'import/prefer-default-export': 'off', + /** + * Forcing to have an href attribute directly conflicts with + * NextJS's next/link Link element best practices. In this project, Use + * next/link's Link with valid attributes with a bare child element. + */ + 'jsx-a11y/anchor-is-valid': 'off', + /** + * The default, for some reason, is to have a space between the closing + * bracket and the slash. This creates code that looks like `
`. + * IMHO, this is repulsive, so I am confidently declaring the use of + * whitespace before the self-closing slash instead - `
` + */ + 'react/jsx-tag-spacing': [1, { + closingSlash: 'never', // < /Element> or + afterOpening: 'never', // < Element> or < Element/> + beforeClosing: 'never', // + beforeSelfClosing: 'always', // + }], + /** + * Function components are superior, that is all. + */ + 'react/function-component-definition': [2, { namedComponents: 'arrow-function' }], + /** + * It's really up to the project maintainers to say what is and is not + * 'restricted' in a language, tbh. Just do good code reviews is what I say. + */ + 'no-restricted-syntax': 'off', + }, +}; diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 0000000..5d35c34 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +yarn tsc --noEmit && yarn eslint --fix . diff --git a/.storybook/main.js b/.storybook/main.js deleted file mode 100644 index 3ec3030..0000000 --- a/.storybook/main.js +++ /dev/null @@ -1,11 +0,0 @@ -module.exports = { - "stories": [ - "../stories/**/*.stories.mdx", - "../stories/**/*.stories.@(js|jsx|ts|tsx)", - '../components/**/*.stories.@(js|jsx|ts|tsx)' - ], - "addons": [ - "@storybook/addon-links", - "@storybook/addon-essentials" - ] -} \ No newline at end of file diff --git a/.storybook/preview.js b/.storybook/preview.js deleted file mode 100644 index a833ced..0000000 --- a/.storybook/preview.js +++ /dev/null @@ -1,16 +0,0 @@ -import { library } from '@fortawesome/fontawesome-svg-core'; -import { fab } from '@fortawesome/free-brands-svg-icons'; -import { fas } from '@fortawesome/free-solid-svg-icons'; -import { far } from '@fortawesome/free-regular-svg-icons' - -library.add(fab, fas, far); - -export const parameters = { - actions: { argTypesRegex: "^on[A-Z].*" }, - controls: { - matchers: { - color: /(background|color)$/i, - date: /Date$/, - }, - }, -} diff --git a/README.md b/README.md index 071d67d..d5b35a7 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ open-source [Next.js](https://nextjs.org/) project developed and maintained by the volunteer-run CIMUN Department of Technology. Chicago International Model United Nations (CIMUN) is an annual inter-scholastic -(high school) Model UN conference in Chicago, IL. Our focus is to create realistic +(high school) Model UN conference in Chicago, IL. Our focus is to create realistic educational opportunities for young leaders in the scholastic MUN community. We are organized by the Model United Nations Development Organization (MUNDO). @@ -26,7 +26,7 @@ our continuous deployment needs.

- Vercel Banner + Vercel Banner

diff --git a/app/api/AxiosHandler.ts b/app/api/AxiosHandler.ts new file mode 100644 index 0000000..5c34e55 --- /dev/null +++ b/app/api/AxiosHandler.ts @@ -0,0 +1,48 @@ +/** + * A simple implementation of an Axios-based request handler. + */ +import axios, { AxiosRequestConfig, AxiosResponse, Method } from 'axios'; + +export interface IAxiosHandler { + callRaw(url: string, method: Method, data?: D, token?: string, + axiosConfig?: AxiosRequestConfig): Promise>; + + get(url: string, axiosConfig?: AxiosRequestConfig): Promise; + + post(url: string, data?: D, axiosConfig?: AxiosRequestConfig): Promise; +} + +export default class AxiosHandler implements IAxiosHandler { + callRaw = ( + url: string, + method: Method, + data?: D, + token?: string, + axiosConfig?: AxiosRequestConfig) + : Promise> => { + // Create required request params + const params: AxiosRequestConfig = { + method, + url, + }; + // Handle assigning auth header + if (token) { + params.headers = { Authorization: `Bearer ${token}` }; + } + if (data) { + params.data = data; + } + // Perform request and return promise + return axios.request({ ...params, ...axiosConfig }); + }; + + get = async (url: string, axiosConfig?: AxiosRequestConfig): Promise => { + const res = await this.callRaw(url, 'GET', null, null, axiosConfig); + return res.data; + }; + + post = async (url: string, data?: unknown, axiosConfig?: AxiosRequestConfig): Promise => { + const res = await this.callRaw(url, 'POST', data, null, axiosConfig); + return res.data; + }; +} diff --git a/app/api/MundoClient.ts b/app/api/MundoClient.ts new file mode 100644 index 0000000..c2a9270 --- /dev/null +++ b/app/api/MundoClient.ts @@ -0,0 +1,41 @@ +/** + * Client to use for MUNDO API requests. + */ +import AxiosHandler, { IAxiosHandler } from '_api/AxiosHandler'; +import FetchFormResponseDto from '_api/dto/FetchFormResponse.dto'; +import SubmitFormDto from '_api/dto/SubmitForm.dto'; +import Globs from '_constants/globs'; +import SubmitFormResponseDto from '_api/dto/SubmitFormResponse.dto'; + +export interface IMundoClient extends IAxiosHandler { + PREFIX: string; + + getFormTemplate(confId: string, formId: string): Promise; + + postFormSubmission(confId: string, formId: string, + submitFormDto: SubmitFormDto): Promise +} + +export default class MundoClient extends AxiosHandler implements IMundoClient { + PREFIX = Globs.API.URL_PREFIX; + + /* + * Retrieves data for a form to be rendered + */ + getFormTemplate = async (confId: string, formId: string): Promise => { + const url = `${this.PREFIX}/conferences/${confId}/forms/${formId}`; + return this.get(url); + }; + + /** + * POSTs a submission for a form + */ + postFormSubmission = async ( + confId: string, + formId: string, + submitFormDto: SubmitFormDto, + ): Promise => { + const url = `${this.PREFIX}/conferences/${confId}/forms/${formId}/submissions`; + return this.post(url, submitFormDto); + }; +} diff --git a/app/api/dto/Base.dto.ts b/app/api/dto/Base.dto.ts new file mode 100644 index 0000000..4efbeed --- /dev/null +++ b/app/api/dto/Base.dto.ts @@ -0,0 +1,13 @@ +export default class BaseDto { + id: number; + + active: boolean; + + createdBy: number; + + updatedBy: number; + + createdOn: Date; + + updatedOn: Date; +} diff --git a/app/api/dto/FetchFormResponse.dto.ts b/app/api/dto/FetchFormResponse.dto.ts new file mode 100644 index 0000000..52dcf7b --- /dev/null +++ b/app/api/dto/FetchFormResponse.dto.ts @@ -0,0 +1,5 @@ +import FormSectionDto from '_api/dto/FormSection.dto'; + +export default class FetchFormResponseDto { + sections: Array; +} diff --git a/app/api/dto/FormField.dto.ts b/app/api/dto/FormField.dto.ts new file mode 100644 index 0000000..78d562d --- /dev/null +++ b/app/api/dto/FormField.dto.ts @@ -0,0 +1,20 @@ +import BaseDto from '_api/dto/Base.dto'; +import FormFieldValueDto from '_api/dto/FormFieldValue.dto'; + +export default class FormFieldDto extends BaseDto { + fieldType: string; + + required: boolean; + + // the index for the field in the form itself + index: number; + + // the "question" + content: string; + + // optional description + description?: string; + + // optional values to select + values: FormFieldValueDto[]; +} diff --git a/app/api/dto/FormFieldResponse.dto.ts b/app/api/dto/FormFieldResponse.dto.ts new file mode 100644 index 0000000..c67e2c6 --- /dev/null +++ b/app/api/dto/FormFieldResponse.dto.ts @@ -0,0 +1,7 @@ +class FormFieldResponseDto { + id: number; + + response: unknown; +} + +export default FormFieldResponseDto; diff --git a/app/api/dto/FormFieldValue.dto.ts b/app/api/dto/FormFieldValue.dto.ts new file mode 100644 index 0000000..84b14d9 --- /dev/null +++ b/app/api/dto/FormFieldValue.dto.ts @@ -0,0 +1,7 @@ +export default class FormFieldValueDto { + id: number; + + fieldId: number; + + value: string; +} diff --git a/app/api/dto/FormSection.dto.ts b/app/api/dto/FormSection.dto.ts new file mode 100644 index 0000000..12c6f4a --- /dev/null +++ b/app/api/dto/FormSection.dto.ts @@ -0,0 +1,16 @@ +import BaseDto from '_api/dto/Base.dto'; +import FormFieldDto from '_api/dto/FormField.dto'; + +export default class FormSectionDto extends BaseDto { + formId: number; + + title: string; + + subtitle?: string; + + intro?: string; + + outro?: string; + + fields: Array; +} diff --git a/app/api/dto/SubmitForm.dto.ts b/app/api/dto/SubmitForm.dto.ts new file mode 100644 index 0000000..2fd8008 --- /dev/null +++ b/app/api/dto/SubmitForm.dto.ts @@ -0,0 +1,5 @@ +import FormFieldResponseDto from '_api/dto/FormFieldResponse.dto'; + +export default class SubmitFormDto { + responses: Array; +} diff --git a/app/api/dto/SubmitFormResponse.dto.ts b/app/api/dto/SubmitFormResponse.dto.ts new file mode 100644 index 0000000..0d5c1e1 --- /dev/null +++ b/app/api/dto/SubmitFormResponse.dto.ts @@ -0,0 +1,9 @@ +import BaseDto from '_api/dto/Base.dto'; + +export default class SubmitFormResponseDto extends BaseDto { + formId: number; + + // TODO: The schema for this is known, but is not necessary on submission. + // As a result, please reevaluate this decision as development continues. + responses: Array; +} diff --git a/app/components/footer/Footer.stories.tsx b/app/components/footer/Footer.stories.tsx new file mode 100644 index 0000000..763b1e9 --- /dev/null +++ b/app/components/footer/Footer.stories.tsx @@ -0,0 +1,13 @@ +import React from "react"; +// import { Story, Meta } from "@storybook/react"; +import Footer from "./index"; + +export default { + title: "Layout/Footer", + component: Footer, +} +// } as Meta; + +// const Template: Story = (args) =>