Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactoring components #66

Draft
wants to merge 100 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
100 commits
Select commit Hold shift + click to select a range
f8755cf
Extracted API requests for sendLoginCode and register
Nautman Apr 1, 2021
3e6fe3c
Changed all instances of axios to API axios
Nautman Apr 1, 2021
bc51e8a
Added delete request to API instance + getAdmins + delete request
Nautman Apr 2, 2021
6fdf663
Added ability to see applicant email
Nautman Apr 3, 2021
19dca2a
Added files slice
Nautman Apr 3, 2021
4529fd5
Refactored Upload to allow for greater customisation and reusability
Nautman Apr 3, 2021
9251468
Bug fix
Nautman Apr 3, 2021
352e3ae
Added custom useAxios and improved admin hooks
Nautman Apr 4, 2021
88998e4
Improved admin hooks
Nautman Apr 4, 2021
2f075b6
Flattened admin file structure
Nautman Apr 4, 2021
3eba4b8
Improved admin grading types and added useApi
Nautman Apr 4, 2021
bd1d8b1
Restructured admi requests to api structure
Nautman Apr 4, 2021
8885416
Moved files and fixed errors
Nautman Apr 5, 2021
dd7c2ba
Merge branch 'add-applicant-information' into refactor
Nautman Apr 5, 2021
7de6e09
Created recommendationsSlice
Nautman Apr 5, 2021
0b5f3b7
Merge branch 'add-applicant-information' into refactor
Nautman Apr 5, 2021
ed304a6
Fixed warnings
Nautman Apr 5, 2021
bc55805
Improved references
Nautman Apr 5, 2021
98765a1
Made axios calls into api calls
Nautman Apr 5, 2021
80b7a03
Extracted Chapters and References from Portal
Nautman Apr 5, 2021
d2286bc
Extracted survey from portal
Nautman Apr 5, 2021
feb1952
Extracted Introduction from portal
Nautman Apr 5, 2021
4339b2e
Extracted survey hooks
Nautman Apr 5, 2021
ab8cdd9
Moved NoMatch
Nautman Apr 5, 2021
7ac9072
Extracted Reference
Nautman Apr 5, 2021
58f476f
Improved redux state
Nautman Apr 5, 2021
9674335
Added progress bars to two places
Nautman Apr 5, 2021
3b1b23d
Changed name of FileChapters export
Nautman Apr 5, 2021
cebfb45
Changed FileChapters into TranslatedChapters to improve resuability
Nautman Apr 5, 2021
e409541
Moved disabled loading logic from FileChapters to Upload feature
Nautman Apr 5, 2021
b2a2ebd
Extracted downloadFile from Upload
Nautman Apr 5, 2021
87128ce
Add comments to useFiles
Nautman Apr 5, 2021
ab29bd0
Added comments to filesSlice
Nautman Apr 5, 2021
8220ea2
Added comments to api/files
Nautman Apr 5, 2021
8e4353f
Bug fix to FileChapters
Nautman Apr 5, 2021
1a439c3
Commented Upload
Nautman Apr 5, 2021
59b87fc
Added translation to Upload and disabled property to Button in Contac…
Nautman Apr 5, 2021
7832f3a
Bug fix for survey redux
Nautman Apr 5, 2021
7f1f073
Improved Chapter code style
Nautman Apr 5, 2021
83dab6f
Added comments to Chapter and CopyLoginCode
Nautman Apr 5, 2021
ec3f240
Imported bootstrap css to storybook
Nautman Apr 5, 2021
2e7816f
Fixed storybook css import bug
Nautman Apr 5, 2021
8f06ef1
Added error field for firstName and lastName in AdminContact
Nautman Apr 9, 2021
462a706
Began adding comments to API calls
Nautman May 4, 2021
4ca8a90
Flattened components folder
Nautman May 6, 2021
f2eccec
Deleted unused footer component
Nautman May 6, 2021
d71be08
Fixed import problems
Nautman May 7, 2021
755f895
Converted Chapter story to TypeScript
Nautman May 8, 2021
6444383
Converted AddButton story to TypeScript
Nautman May 9, 2021
cc134d1
Converted ContactPerson story to TypeScript
Nautman May 10, 2021
d7b7a01
Converted GradingData story to TypeScript
Nautman May 11, 2021
3d959ee
Fixed type bug in GradingData story
Nautman May 12, 2021
461d3a5
Converted GradingModal story to TypeScript
Nautman May 13, 2021
b2a7cf8
Converted OpenPDF story to TypeScript
Nautman May 14, 2021
22e3ed4
Added Story args to AdminContact
Nautman May 15, 2021
1a0fb88
Exported RatingProps
Nautman May 15, 2021
95ac3dc
Made onDownload prop optional in OpenPDF
Nautman May 15, 2021
0a0c20c
Added controls to AddButton story
Nautman May 15, 2021
dca0ecb
Revert "Made onDownload prop optional in OpenPDF"
Nautman May 15, 2021
62f5d6d
Converted Rating story to TypeScript
Nautman May 16, 2021
b9e75f7
Converted StyledGroup story to TypeScript
Nautman May 17, 2021
14cc64a
Converted Survey story to TypeScript
Nautman May 18, 2021
b0cf8ee
Converted Plate component to TypeScript
Nautman May 20, 2021
de5e59e
Convert Star component to TypeScript and renamed it to AnimatedStar
Nautman May 21, 2021
2c4366a
Converted StyledTitle to TypeScript
Nautman May 21, 2021
2d8cc8b
Converted service worker code to TypeScript
Nautman May 21, 2021
69fad00
Converted test code to TypeScript
Nautman May 21, 2021
ae5fa00
Converted StyledGroup to TypeScript
Nautman May 21, 2021
3ac05fa
Fixed StyledGroup error
Nautman May 21, 2021
e4970d6
Fixed warnings in stories
Nautman May 21, 2021
934c7e0
Removed unused default react logo
Nautman May 22, 2021
a349e2d
Moved logo to config folder
Nautman May 23, 2021
18f3b38
Moved portal translations to config and removed unnecessary translations
Nautman May 24, 2021
2e8b7dc
Fixed incorrect import in Logo
Nautman May 24, 2021
53ae5e5
Added deadline to config
Nautman May 25, 2021
17fedca
Deleted unnecessary code in GradingView
Nautman May 26, 2021
7f984c3
Simplified survey component
Nautman May 27, 2021
4a0632b
Began working on a custom survey feature
Nautman May 28, 2021
56e2cc9
Fixed type bug in CustomSurvey story
Nautman May 29, 2021
1952416
Added survey questions to config
Nautman May 30, 2021
44cd895
Added select question type to survey type definitions
Nautman May 30, 2021
534c77e
Developed question type react components in CustomSurvey
Nautman May 30, 2021
6092256
Added range translation and component to CustomSurvey
Nautman May 30, 2021
9fead6d
Separated CustomSurveyAccordion into a Form and Question
Nautman May 31, 2021
ef3b802
Added initialValues to CustomSurvey
Nautman Jun 1, 2021
23fcd4d
Bug fix for initialValues in CustomSurvey
Nautman Jun 1, 2021
f28bfdf
Added submit button and required fields to CustomSurvey
Nautman Jun 2, 2021
f1316b1
Made CustomSurveyAccordionProps the same as props for Form
Nautman Jun 2, 2021
901c9a2
Moved chapter JSON into chapters key in translations
Nautman Jun 3, 2021
6a11d5b
Began to make config even more customizable
Nautman Jun 4, 2021
e4d1b6c
Began working on more customisable Chapters
Nautman Jun 5, 2021
2978c2e
Made the Chapter show a Survey instead of a FileChapter, for now
Nautman Jun 6, 2021
a52ae0b
Added RecommendationChapter to custom chapters
Nautman Jun 7, 2021
13f2463
Fixed bug
Nautman Jun 8, 2021
aae2223
Began implementing the CustomSurvey in reality
Nautman Jun 9, 2021
1eee68f
Temporary solution to include survey in application
Nautman Jun 10, 2021
08f0e58
Bug fix
Nautman Jun 11, 2021
cd614a6
Switched out FileType in favour of a custom id
Nautman Jun 11, 2021
fe48ef4
Fixed duplicate chapter descriptions and added initialValues to survey
Nautman Jun 12, 2021
a0fd4b8
Removed unused uncustomised FileChapters and SurveyChapter
Nautman Jun 13, 2021
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

# storybook
build-storybook.log
storybook-static

# dependencies
/node_modules
Expand Down
2 changes: 2 additions & 0 deletions .storybook/preview.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import "bootstrap/dist/css/bootstrap.min.css";

import CustomThemeProvider from "./../src/components/CustomThemeProvider";
import { I18nextProvider } from "react-i18next";
import i18n from "../src/i18n";
Expand Down
6 changes: 3 additions & 3 deletions src/App.test.jsx → src/App.test.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import App from "./App";
import React from "react";
import { render } from "@testing-library/react";
import App from "./App";

test('renders "Utvecklat av Digital Ungdom"', () => {
test('renders "Digital Ungdom"', () => {
const { getByText } = render(<App />);
const element = getByText(/Svenska/i);
const element = getByText(/Digital Ungdom/i);
expect(element).toBeInTheDocument();
});
27 changes: 5 additions & 22 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import "bootstrap/dist/css/bootstrap.min.css";
import "utils/tokenInterceptor";
import "resources/app.css";
import "react-toastify/dist/ReactToastify.min.css";

import store, { persistor } from "./store";

import AuthenticatedLayer from "features/auth/AuthenticatedLayer";
import Background from "components/Background";
import ChangeLanguage from "features/ChangeLanguage";
import CustomThemeProvider from "components/CustomThemeProvider";
import DevelopedBy from "components/DevelopedBy";
Expand All @@ -13,40 +15,21 @@ import { Provider } from "react-redux";
import React from "react";
import Router from "features/router";
import { ToastContainer } from "react-toastify";
import axios from "axios";
import styled from "styled-components";

const StyledApp = styled.div`
background: ${(props) => props.theme.bg};

height: 100%;
width: 100%;
overflow: auto;

#centered {
width: 100%;
min-height: calc(100% - 2.5rem);
display: table;
}
`;

axios.defaults.baseURL =
process.env.REACT_APP_API_URL || "https://devapi.infrarays.digitalungdom.se";

function App() {
function App(): React.ReactElement {
return (
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<AuthenticatedLayer>
<CustomThemeProvider>
<StyledApp>
<Background>
<ChangeLanguage />
<ToastContainer />
<div id="centered">
<Router />
</div>
<DevelopedBy />
</StyledApp>
</Background>
</CustomThemeProvider>
</AuthenticatedLayer>
</PersistGate>
Expand Down
45 changes: 45 additions & 0 deletions src/api/admin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { Admin, NewAdmin } from "types/user";
import {
Application,
ApplicationGrade,
GradedApplication,
IndividualGrading,
OrderItem,
} from "types/grade";

import api from "./axios";

export const getAdmins = (): Promise<Admin[]> =>
api.format.get<Admin[]>("/admin", { params: { skip: 0, limit: 10 } });

export const getGradesConfig = (applicantID: string): string =>
`/application/${applicantID}/grade`;

export const getGradesByApplicant = (
applicantID: string
): Promise<IndividualGrading[]> =>
api.format.get<IndividualGrading[]>(getGradesConfig(applicantID));

export const getApplications = (): Promise<
(Application | GradedApplication)[]
> => api.format.get<Application[]>("/application");

export const getGradingOrder = (): Promise<OrderItem[]> =>
api.format.get<OrderItem[]>("/admin/grading");

export const postApplicationGrade = (
applicantID: string,
form: ApplicationGrade
): Promise<IndividualGrading> =>
api.format.post<IndividualGrading>(`/application/${applicantID}/grade`, form);

export const addAdmin = (admin: NewAdmin): Promise<Admin> =>
api.format.post<Admin>("/admin", admin);

/**
* Admins can randomise the order they view applications in,
* to decrease bias in the grading process
* @returns order of applicants
*/
export const randomiseOrder = (): Promise<OrderItem[]> =>
api.format.post<OrderItem[]>("/admin/grading/randomise");
29 changes: 29 additions & 0 deletions src/api/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { ServerTokenResponse } from "types/tokens";
import api from "./axios";

export const authorizeWithEmailAndCode = (
email: string,
code: string
): Promise<ServerTokenResponse> =>
api.format.post<ServerTokenResponse>(
"/user/oauth/token",
{
grant_type: "client_credentials",
},
{
headers: { Authorization: `Email ${btoa(email + ":" + code)}` },
}
);

export const authorizeWithToken = (
token: string
): Promise<ServerTokenResponse> =>
api.format.post<ServerTokenResponse>(
"/user/oauth/token",
{
grant_type: "client_credentials",
},
{
headers: { Authorization: `Email ${token}` },
}
);
47 changes: 47 additions & 0 deletions src/api/axios.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import axios, { AxiosRequestConfig } from "axios";

import { DEV_API_BASE_URL } from "./constants";
import formatErrors from "utils/formatErrors";

export const api = axios.create({
baseURL: process.env.REACT_APP_API_URL || DEV_API_BASE_URL,
});

interface FormattedRequests {
post<T>(url: string, data?: unknown, config?: AxiosRequestConfig): Promise<T>;
get<T>(url: string, config?: AxiosRequestConfig): Promise<T>;
delete<T>(url: string, config?: AxiosRequestConfig): Promise<T>;
patch<T>(
url: string,
data?: unknown,
config?: AxiosRequestConfig
): Promise<T>;
}

const format: FormattedRequests = {
post: (url, data, config) =>
api
.post(url, data, config)
.then((res) => res.data)
.catch(formatErrors),
get: (url, config) =>
api
.get(url, config)
.then((res) => res.data)
.catch(formatErrors),
patch: (url, data, config) =>
api
.patch(url, data, config)
.then((res) => res.data)
.catch(formatErrors),
delete: (url, config) =>
api
.delete(url, config)
.then((res) => res.data)
.catch(formatErrors),
};

export default {
...api,
format,
};
1 change: 1 addition & 0 deletions src/api/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const DEV_API_BASE_URL = "https://devapi.infrarays.digitalungdom.se";
25 changes: 25 additions & 0 deletions src/api/downloadPDF.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import api from "./axios";
import showFile from "utils/showFile";

/**
* Downloads PDF and returns the blob and the file name
* @param applicantID
* @returns {[Blob, string]} Blob and file name
*/
export const downloadPDF = (applicantID: string): Promise<[Blob, string]> =>
api
.get<Blob>(`/application/${applicantID}/pdf`, { responseType: "blob" })
.then((res) => {
const name = res.headers["content-disposition"].split("filename=")[1];
return [res.data, name];
});

/**
* Download and open PDF in new tab
* @param applicantID
* @returns void
*/
export const downloadAndOpen = (applicantID: string): Promise<void> =>
downloadPDF(applicantID).then((args) => showFile(...args));

export default downloadPDF;
69 changes: 69 additions & 0 deletions src/api/files.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { FileInfo, FileType } from "types/files";

import FileSaver from "file-saver";
import api from "./axios";

export const getFilesConfiguration = (applicantID = "@me"): string =>
`/application/${applicantID}/file`;

export const getFiles = (applicantID = "@me"): Promise<FileInfo[]> =>
api.format.get<FileInfo[]>(getFilesConfiguration(applicantID));

export const downloadFile = (
fileID: string,
applicantID = "@me"
): Promise<void> =>
api
.get(`application/${applicantID}/file/${fileID}`, {
responseType: "blob",
})
.then((res) => {
const utf8FileName = res.headers["content-disposition"].split(
"filename*=UTF-8''"
)[1];
const decodedName = decodeURIComponent(utf8FileName);
const normalName = res.headers["content-disposition"].split(
"filename="
)[1];
FileSaver.saveAs(
res.data,
utf8FileName === undefined
? normalName.substring(1, normalName.length - 1)
: decodedName.substring(1, decodedName.length - 1)
);
});

export const downloadFullPDF = (applicantID = "@me"): Promise<void> =>
api
.get(`/application/${applicantID}/pdf`, { responseType: "blob" })
.then((res) => {
FileSaver.saveAs(
res.data,
res.headers["content-disposition"].split("filename=")[1]
);
});

export const deleteFile = (
fileID: string,
applicantID = "@me"
): Promise<void> =>
api.format.delete(`/application/${applicantID}/file/${fileID}`);

export const uploadFile = (
fileType: FileType,
file: File,
fileName: string,
applicantID = "@me"
): Promise<FileInfo> => {
// create FormData to append file with desired file name
const form = new FormData();
form.append("file", file, fileName);
// format the results, useful if there are errors!
return api.format.post<FileInfo>(
`application/${applicantID}/file/${fileType}`,
form,
{
headers: { "Content-Type": "multipart/form-data" },
}
);
};
33 changes: 33 additions & 0 deletions src/api/recommendations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import {
RecommendationFile,
RecommendationRequest,
} from "types/recommendations";

import api from "./axios";

export const requestRecommendation = (
recommendationIndex: number,
email: string
): Promise<RecommendationRequest> =>
api.format.post<RecommendationRequest>(
`/application/@me/recommendation/${recommendationIndex}`,
{
email,
}
);

export const getRecommendationRequestConfig = (code: string): string =>
`/application/recommendation/${code}`;

export const uploadLetterOfRecommendation = (
file: File,
fileName: string,
code: string
): Promise<RecommendationFile> => {
const form = new FormData();
form.append("file", file, fileName);
return api.format.post<RecommendationFile>(
`/application/recommendation/${code}`,
form
);
};
17 changes: 17 additions & 0 deletions src/api/register.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Applicant } from "types/user";
import api from "./axios";

/**
* Required values in a registration form for an applicant
*/
export type RegistrationForm = Pick<
Applicant,
"firstName" | "lastName" | "birthdate" | "email" | "finnish"
>;

/**
* Register an application
* @param {RegistrationForm} form values to register with
*/
export const register = (form: RegistrationForm): Promise<Applicant> =>
api.format.post<Applicant>("application", form);
41 changes: 41 additions & 0 deletions src/api/sendLoginCode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import formatErrors, { FormattedErrors } from "utils/formatErrors";

import { DEV_API_BASE_URL } from "./constants";
import api from "./axios";

/**
* Sends login code to your email
* @param {email}
* @returns {Promise<null | string>} returns either nothing or the login code
*/
const sendLoginCode = (email: string): Promise<string | null> =>
api.format.post<null | string>("/user/send_email_login_code", {
email,
});

/**
* Required parameters for send login code request
*/
type SendLoginCodeParams = {
email: string; // the email which you signed up with
};

/**
* Sends login code to your email and displays it in a notification if it is run on the dev api
* @param email
* @returns the string or formatted errors
*/
export const sendLoginCodeAndShowCode = (
email: string
): Promise<string | void | FormattedErrors<SendLoginCodeParams>> =>
api
.post<string | void>("/user/send_email_login_code", { email })
.then((res) => {
if (res.data && res.config.baseURL === DEV_API_BASE_URL) return res.data;
return;
})
.catch((err) => {
throw formatErrors(err);
});

export default sendLoginCode;
Loading