diff --git a/package.json b/package.json index 290a8ba..83556ed 100644 --- a/package.json +++ b/package.json @@ -4,13 +4,16 @@ "private": true, "dependencies": { "@material-ui/core": "^4.10.1", + "@material-ui/lab": "^4.0.0-alpha.55", "@testing-library/jest-dom": "^4.2.4", "@testing-library/react": "^9.3.2", "@testing-library/user-event": "^7.1.2", "@types/jest": "^24.0.0", + "@types/moment": "^2.13.0", "@types/node": "^12.0.0", "@types/react": "^16.9.0", "@types/react-dom": "^16.9.0", + "moment": "^2.26.0", "react": "^16.13.1", "react-dom": "^16.13.1", "react-scripts": "3.4.1", diff --git a/src/App.css b/src/App.css deleted file mode 100644 index 74b5e05..0000000 --- a/src/App.css +++ /dev/null @@ -1,38 +0,0 @@ -.App { - text-align: center; -} - -.App-logo { - height: 40vmin; - pointer-events: none; -} - -@media (prefers-reduced-motion: no-preference) { - .App-logo { - animation: App-logo-spin infinite 20s linear; - } -} - -.App-header { - background-color: #282c34; - min-height: 100vh; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - font-size: calc(10px + 2vmin); - color: white; -} - -.App-link { - color: #61dafb; -} - -@keyframes App-logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} diff --git a/src/App.test.tsx b/src/App.test.tsx deleted file mode 100644 index 4db7ebc..0000000 --- a/src/App.test.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import React from 'react'; -import { render } from '@testing-library/react'; -import App from './App'; - -test('renders learn react link', () => { - const { getByText } = render(); - const linkElement = getByText(/learn react/i); - expect(linkElement).toBeInTheDocument(); -}); diff --git a/src/App.tsx b/src/App.tsx index d919f03..1ec6596 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,25 +1,21 @@ import React from 'react'; -import logo from './logo.svg'; -import './App.css'; +import { Container, ThemeProvider, Typography } from "@material-ui/core"; +import { CompanyForm } from "./forms/company"; +import { studentTheme } from "./ui/theme"; function App() { return ( -
-
- logo -

- Edit src/App.tsx and save to reload. -

- - Learn React - System Praktyk - -
-
+ +
+ + Zgłoszenie Praktyki + UX Demo + + Miejsce odbywania praktyki + + +
+
); } diff --git a/src/data/common.ts b/src/data/common.ts new file mode 100644 index 0000000..62671b8 --- /dev/null +++ b/src/data/common.ts @@ -0,0 +1,3 @@ +export interface Identifiable { + id?: string +} diff --git a/src/data/company.ts b/src/data/company.ts new file mode 100644 index 0000000..5e6f2db --- /dev/null +++ b/src/data/company.ts @@ -0,0 +1,41 @@ +import { Identifiable } from "./common"; + +export interface Address { + street: string; + building: string; + city: string; + postalCode: string; + country: string; +} + +export interface Company extends Identifiable { + name: string; + url?: string; + nip: string; + offices: BranchOffice[]; +} + +export interface BranchOffice extends Identifiable { + address: Address; +} + +export const emptyCompany: Company = { + offices: [], + url: "", + name: "", + nip: "", +} + +export const emptyAddress: Address = { + street: "", + city: "", + country: "", + postalCode: "", + building: "" +} + +export const emptyBranchOffice: BranchOffice = { + address: emptyAddress, +} + +export const formatAddress = ({ postalCode, building, street, city, country }: Address): string => `${street} ${building}, ${postalCode} ${city}, ${country}` diff --git a/src/data/internship.ts b/src/data/internship.ts new file mode 100644 index 0000000..03a557e --- /dev/null +++ b/src/data/internship.ts @@ -0,0 +1,23 @@ +import { Moment } from "moment"; +import { Nullable } from "../helpers"; + +export enum InternshipType { } +export enum InternshipProgram { } + +export interface Internship { + type: InternshipType; + program: InternshipProgram; + startDate: Moment; + endDate: Moment; + isAccepted: boolean; + lengthInWeeks: number; +} + +export const emptyInternship: Nullable = { + endDate: null, + startDate: null, + type: null, + program: null, + isAccepted: false, + lengthInWeeks: 0, +} diff --git a/src/forms/Internship.tsx b/src/forms/Internship.tsx new file mode 100644 index 0000000..e749b8d --- /dev/null +++ b/src/forms/Internship.tsx @@ -0,0 +1,22 @@ +import React from "react"; +import { useFormik } from "formik"; +import { emptyInternship, Internship } from "../data/internship"; +import { Nullable } from "../helpers"; +import { TextField } from "@material-ui/core"; + +export type InternshipFormProps = { + +} + +export const InternshipForm: React.FunctionComponent = props => { + const formik = useFormik>({ onSubmit: values => console.log(values), initialValues: emptyInternship }); + + const formikProps = (prop: keyof Internship) => ({ onChange: formik.handleChange, value: formik.values[prop], name: prop }) + + return ( +
+ +
+ ) +} + diff --git a/src/forms/company.tsx b/src/forms/company.tsx new file mode 100644 index 0000000..78729e1 --- /dev/null +++ b/src/forms/company.tsx @@ -0,0 +1,139 @@ +import React, { HTMLProps, useEffect, useMemo, useState } from "react"; +import { BranchOffice, Company, emptyAddress, emptyBranchOffice, emptyCompany, formatAddress } from "../data/company"; +import { sampleCompanies } from "../provider/company"; +import { Autocomplete } from "@material-ui/lab"; +import { Grid, TextField, Typography } from "@material-ui/core"; +import { formFieldProps } from "./helpers"; + +export type CompanyFormProps = {} + +export type BranchOfficeProps = { + company: Company, + disabled?: boolean +} + +export const CompanyItem = ({ company, ...props }: { company: Company } & HTMLProps) => ( +
+
{ company.name }
+ NIP: { company.nip } +
+) + +export const OfficeItem = ({ office, ...props }: { office: BranchOffice } & HTMLProps) => ( +
+
{ office.address.city }
+ { formatAddress(office.address) } +
+) + +export const BranchForm: React.FC = ({ company, disabled = false }) => { + const [office, setOffice] = useState(emptyBranchOffice) + + const canEdit = useMemo(() => !office.id && !disabled, [office.id, disabled]); + + const fieldProps = formFieldProps(office.address, address => setOffice({ ...office, address })) + + const handleCityChange = (event: any, value: BranchOffice | string | null) => { + if (typeof value === "string") { + setOffice({ + ...emptyBranchOffice, + address: { + ...emptyAddress, + city: value, + } + }); + } else if (typeof value === "object" && value !== null) { + setOffice(value); + } else { + setOffice(emptyBranchOffice); + } + } + + const handleCityInput = (event: any, value: string) => { + const base = office.id ? emptyBranchOffice : office; + setOffice({ + ...base, + address: { + ...base.address, + city: value, + } + }) + } + + useEffect(() => void (office.id && setOffice(emptyBranchOffice)), [company]) + + return ( + + + typeof office == "string" ? office : office.address.city } + renderOption={ office => } + renderInput={ props => } + onChange={ handleCityChange } + onInputChange={ handleCityInput } + inputValue={ office.address.city } + value={ office.id ? office : null } + freeSolo + /> + + + + + + + + + + + + + + + ) +} + +export const CompanyForm: React.FunctionComponent = props => { + const [company, setCompany] = useState(emptyCompany); + const canEdit = useMemo(() => !company.id, [company.id]); + + const fieldProps = formFieldProps(company, setCompany) + + const handleCompanyChange = (event: any, value: Company | string | null) => { + if (typeof value === "string") { + setCompany({ + ...emptyCompany, + name: value, + }); + } else if (typeof value === "object" && value !== null) { + setCompany(value); + } else { + setCompany(emptyCompany); + } + } + + return ( + <> + + + option.name } + renderOption={ company => } + renderInput={ props => } + onChange={ handleCompanyChange } + freeSolo + /> + + + + + + + + + Oddział + + + ) +} + diff --git a/src/forms/helpers.ts b/src/forms/helpers.ts new file mode 100644 index 0000000..4fafb88 --- /dev/null +++ b/src/forms/helpers.ts @@ -0,0 +1,20 @@ +import { DOMEvent } from "../helpers"; + +type UpdatingEvent = "onBlur" | "onChange" | "onInput"; +type FormFieldHelperOptions = { + event: UpdatingEvent +} + +export function formFieldProps(subject: T, update: (value: T) => void, options: Partial> = {}) { + const { + event = "onChange" + } = options; + + return (field: keyof T) => ({ + value: subject[field], + [event]: (event: DOMEvent) => update({ + ...subject, + [field]: event.target.value, + } as T) + }) +} diff --git a/src/helpers.ts b/src/helpers.ts new file mode 100644 index 0000000..e128e6c --- /dev/null +++ b/src/helpers.ts @@ -0,0 +1,5 @@ +export type Nullable = { [P in keyof T]: T[P] | null } + +export interface DOMEvent extends Event { + target: TTarget; +} diff --git a/src/index.css b/src/index.css deleted file mode 100644 index ec2585e..0000000 --- a/src/index.css +++ /dev/null @@ -1,13 +0,0 @@ -body { - margin: 0; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', - sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -code { - font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', - monospace; -} diff --git a/src/index.tsx b/src/index.tsx index f5185c1..4146d1d 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,6 +1,5 @@ import React from 'react'; import ReactDOM from 'react-dom'; -import './index.css'; import App from './App'; import * as serviceWorker from './serviceWorker'; diff --git a/src/logo.svg b/src/logo.svg deleted file mode 100644 index 6b60c10..0000000 --- a/src/logo.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/src/provider/company.ts b/src/provider/company.ts new file mode 100644 index 0000000..be861f9 --- /dev/null +++ b/src/provider/company.ts @@ -0,0 +1,74 @@ +import { Company } from "../data/company"; +import { makeIdSequence } from "./helpers"; + +const companySequence = makeIdSequence(); +const officeSequence = makeIdSequence(); + +export const sampleCompanies: Company[] = [ + { + id: companySequence(), + name: "Intel", + url: "https://www.intel.com/content/www/us/en/jobs/locations/poland.html", + nip: "9570752316", + offices: [{ + id: officeSequence(), + address: { + city: "Gdańsk", + street: "ul. Słowackiego", + building: "173", + postalCode: "80-298", + country: "Poland" + } + }] + }, + { + id: companySequence(), + name: "IHS Markit", + url: "http://ihsgdansk.com/", + nip: "5842068320", + offices: [{ + id: officeSequence(), + address: { + city: "Gdańsk", + street: "ul. Marynarki Polskiej", + building: "163", + postalCode: "80-868", + country: "Poland" + } + }] + }, + { + id: companySequence(), + name: "Asseco Poland", + url: "http://pl.asseco.com/", + nip: "5842068320", + offices: [{ + id: officeSequence(), + address: { + city: "Gdynia", + street: "ul. Podolska", + building: "21", + postalCode: "81-321", + country: "Poland" + } + }, { + id: officeSequence(), + address: { + city: "Łódź", + street: "al. Marszałka Józefa Piłsudskiego", + building: "85", + postalCode: "90-332", + country: "Poland" + } + }, { + id: officeSequence(), + address: { + city: "Wrocław", + street: "Traugutta", + building: "1/7", + postalCode: "50-449", + country: "Poland" + } + }] + } +] diff --git a/src/provider/helpers.ts b/src/provider/helpers.ts new file mode 100644 index 0000000..20b4496 --- /dev/null +++ b/src/provider/helpers.ts @@ -0,0 +1,4 @@ +export const makeIdSequence = (start: number = 1) => { + let i = start; + return () => i++; +} diff --git a/src/setupTests.ts b/src/setupTests.ts deleted file mode 100644 index 74b1a27..0000000 --- a/src/setupTests.ts +++ /dev/null @@ -1,5 +0,0 @@ -// jest-dom adds custom jest matchers for asserting on DOM nodes. -// allows you to do things like: -// expect(element).toHaveTextContent(/react/i) -// learn more: https://github.com/testing-library/jest-dom -import '@testing-library/jest-dom/extend-expect'; diff --git a/src/ui/theme.ts b/src/ui/theme.ts new file mode 100644 index 0000000..05e110a --- /dev/null +++ b/src/ui/theme.ts @@ -0,0 +1,10 @@ +import { createMuiTheme } from "@material-ui/core"; + +export const studentTheme = createMuiTheme({ + props: { + MuiGrid: { + spacing: 4, + xs: 12, + } + } +}) diff --git a/yarn.lock b/yarn.lock index c32e217..1489c71 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1309,6 +1309,17 @@ react-is "^16.8.0" react-transition-group "^4.4.0" +"@material-ui/lab@^4.0.0-alpha.55": + version "4.0.0-alpha.55" + resolved "https://registry.yarnpkg.com/@material-ui/lab/-/lab-4.0.0-alpha.55.tgz#82a850dc59654e04ee3a31be1b34eb3bf64d5585" + integrity sha512-mPHiS52Z1AT6NlWNp07xxTkoKGU3DZhoGdVtLKGy2+uMH5t4/UPtiZLRab7hR3hvuHvguxgV4tkBC9ww3xqUzA== + dependencies: + "@babel/runtime" "^7.4.4" + "@material-ui/utils" "^4.9.6" + clsx "^1.0.4" + prop-types "^15.7.2" + react-is "^16.8.0" + "@material-ui/styles@^4.10.0": version "4.10.0" resolved "https://registry.yarnpkg.com/@material-ui/styles/-/styles-4.10.0.tgz#2406dc23aa358217aa8cc772e6237bd7f0544071" @@ -1612,6 +1623,13 @@ resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== +"@types/moment@^2.13.0": + version "2.13.0" + resolved "https://registry.yarnpkg.com/@types/moment/-/moment-2.13.0.tgz#604ebd189bc3bc34a1548689404e61a2a4aac896" + integrity sha1-YE69GJvDvDShVIaJQE5hoqSqyJY= + dependencies: + moment "*" + "@types/node@*": version "13.9.2" resolved "https://registry.yarnpkg.com/@types/node/-/node-13.9.2.tgz#ace1880c03594cc3e80206d96847157d8e7fa349" @@ -7017,6 +7035,11 @@ mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@~0.5.1: dependencies: minimist "^1.2.5" +moment@*, moment@^2.26.0: + version "2.26.0" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.26.0.tgz#5e1f82c6bafca6e83e808b30c8705eed0dcbd39a" + integrity sha512-oIixUO+OamkUkwjhAVE18rAMfRJNsNe/Stid/gwHSOfHrOtw9EhAY2AHvdKZ/k/MggcYELFCJz/Sn2pL8b8JMw== + move-concurrently@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92"