renamed
This commit is contained in:
Generated
+29561
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"name": "my-app",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@testing-library/jest-dom": "^5.16.5",
|
||||
"@testing-library/react": "^13.4.0",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
"@types/jest": "^27.5.2",
|
||||
"@types/node": "^16.18.37",
|
||||
"@types/react": "^18.2.14",
|
||||
"@types/react-dom": "^18.2.6",
|
||||
"axios": "^1.4.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-scripts": "5.0.1",
|
||||
"typescript": "^4.9.5",
|
||||
"web-vitals": "^2.1.4"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app",
|
||||
"react-app/jest"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"short_name": "React App",
|
||||
"name": "Create React App Sample",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
},
|
||||
{
|
||||
"src": "logo192.png",
|
||||
"type": "image/png",
|
||||
"sizes": "192x192"
|
||||
},
|
||||
{
|
||||
"src": "logo512.png",
|
||||
"type": "image/png",
|
||||
"sizes": "512x512"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
build
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:@typescript-eslint/recommended-requiring-type-checking"
|
||||
],
|
||||
"plugins": ["@typescript-eslint"],
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es6": true,
|
||||
"node": true
|
||||
},
|
||||
"rules": {
|
||||
"@typescript-eslint/semi": ["error"],
|
||||
"@typescript-eslint/explicit-function-return-type": "off",
|
||||
"@typescript-eslint/explicit-module-boundary-types": "off",
|
||||
"@typescript-eslint/restrict-template-expressions": "off",
|
||||
"@typescript-eslint/restrict-plus-operands": "off",
|
||||
"@typescript-eslint/no-unsafe-member-access": "off",
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"error",
|
||||
{ "argsIgnorePattern": "^_" }
|
||||
],
|
||||
"no-case-declarations": "off"
|
||||
},
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"project": "./tsconfig.json"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import { DiaryEntry } from "../src/types";
|
||||
import toNewDiaryEntry from "../src/utils";
|
||||
|
||||
const data = [
|
||||
{
|
||||
"id": 1,
|
||||
"date": "2017-01-01",
|
||||
"weather": "rainy",
|
||||
"visibility": "poor",
|
||||
"comment": "Pretty scary flight, I'm glad I'm alive"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"date": "2017-04-01",
|
||||
"weather": "sunny",
|
||||
"visibility": "good",
|
||||
"comment": "Everything went better than expected, I'm learning much"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"date": "2017-04-15",
|
||||
"weather": "windy",
|
||||
"visibility": "good",
|
||||
"comment": "I'm getting pretty confident although I hit a flock of birds"
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"date": "2017-05-11",
|
||||
"weather": "cloudy",
|
||||
"visibility": "good",
|
||||
"comment": "I almost failed the landing but I survived"
|
||||
}
|
||||
];
|
||||
|
||||
const diaryEntries: DiaryEntry [] = data.map(obj => {
|
||||
const object = toNewDiaryEntry(obj) as DiaryEntry;
|
||||
object.id = obj.id;
|
||||
return object;
|
||||
});
|
||||
|
||||
export default diaryEntries;
|
||||
Generated
+4921
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "ilaris-diaries",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"tsc": "tsc",
|
||||
"dev": "ts-node-dev src/index.ts",
|
||||
"lint": "eslint --ext .ts .",
|
||||
"start": "node build/index.js"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@types/express": "^4.17.17",
|
||||
"@typescript-eslint/eslint-plugin": "^5.51.0",
|
||||
"@typescript-eslint/parser": "^5.51.0",
|
||||
"eslint": "^8.33.0",
|
||||
"ts-node-dev": "^2.0.0",
|
||||
"typescript": "^4.9.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"express": "^4.18.2"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import express from 'express';
|
||||
const app = express();
|
||||
import diaryRouter from './routes/diaries';
|
||||
app.use(express.json());
|
||||
|
||||
const PORT = 3003;
|
||||
|
||||
app.get('/ping', (_req, res) => {
|
||||
console.log('someone pinged here');
|
||||
res.send('pong');
|
||||
});
|
||||
|
||||
app.use('/api/diaries', diaryRouter);
|
||||
|
||||
app.listen(PORT, () => {
|
||||
console.log(`Server running on port ${PORT}`);
|
||||
});
|
||||
@@ -0,0 +1,37 @@
|
||||
import express from 'express';
|
||||
|
||||
import diaryService from '../services/diaryService';
|
||||
|
||||
import toNewDiaryEntry from '../utils';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.get('/', (_req, res) => {
|
||||
res.send(diaryService.getNonSensitiveEntries());
|
||||
});
|
||||
|
||||
router.get('/:id', (req, res) => {
|
||||
const diary = diaryService.findById(Number(req.params.id));
|
||||
|
||||
if (diary) {
|
||||
res.send(diary);
|
||||
} else {
|
||||
res.sendStatus(404);
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/', (req, res) => {
|
||||
try {
|
||||
const newDiaryEntry = toNewDiaryEntry(req.body);
|
||||
const addedEntry = diaryService.addDiary(newDiaryEntry);
|
||||
res.json(addedEntry);
|
||||
} catch (error: unknown) {
|
||||
let errorMessage = 'Something went wrong.';
|
||||
if (error instanceof Error) {
|
||||
errorMessage += ' Error: ' + error.message;
|
||||
}
|
||||
res.status(400).send(errorMessage);
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
@@ -0,0 +1,42 @@
|
||||
import diaryData from '../../data/entries';
|
||||
|
||||
import {
|
||||
NonSensitiveDiaryEntry, DiaryEntry, NewDiaryEntry
|
||||
} from '../types';
|
||||
|
||||
const diaries: DiaryEntry[] = diaryData;
|
||||
|
||||
const getEntries = (): DiaryEntry[] => {
|
||||
return diaries;
|
||||
};
|
||||
|
||||
const getNonSensitiveEntries = (): NonSensitiveDiaryEntry[] => {
|
||||
return diaries.map(({ id, date, weather, visibility }) => ({
|
||||
id,
|
||||
date,
|
||||
weather,
|
||||
visibility,
|
||||
}));
|
||||
};
|
||||
|
||||
const findById = (id: number): DiaryEntry | undefined => {
|
||||
const entry = diaries.find(d => d.id === id);
|
||||
return entry;
|
||||
};
|
||||
|
||||
const addDiary = ( entry: NewDiaryEntry ): DiaryEntry => {
|
||||
const newDiaryEntry = {
|
||||
id: Math.max(...diaries.map(d => d.id)) + 1,
|
||||
...entry
|
||||
};
|
||||
|
||||
diaries.push(newDiaryEntry);
|
||||
return newDiaryEntry;
|
||||
};
|
||||
|
||||
export default {
|
||||
getEntries,
|
||||
addDiary,
|
||||
getNonSensitiveEntries,
|
||||
findById
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
export enum Weather {
|
||||
Sunny = 'sunny',
|
||||
Rainy = 'rainy',
|
||||
Cloudy = 'cloudy',
|
||||
Stormy = 'stormy',
|
||||
Windy = 'windy',
|
||||
}
|
||||
|
||||
export enum Visibility {
|
||||
Great = 'great',
|
||||
Good = 'good',
|
||||
Ok = 'ok',
|
||||
Poor = 'poor',
|
||||
}
|
||||
|
||||
export interface DiaryEntry {
|
||||
id: number;
|
||||
date: string;
|
||||
weather: Weather;
|
||||
visibility: Visibility;
|
||||
comment: string;
|
||||
}
|
||||
|
||||
export type NewDiaryEntry = Omit<DiaryEntry, 'id'>;
|
||||
|
||||
export type NonSensitiveDiaryEntry = Omit<DiaryEntry, 'comment'>;
|
||||
@@ -0,0 +1,68 @@
|
||||
|
||||
import { NewDiaryEntry, Weather, Visibility } from './types';
|
||||
|
||||
const isString = (text: unknown): text is string => {
|
||||
return typeof text === 'string' || text instanceof String;
|
||||
};
|
||||
|
||||
const parseComment = (comment: unknown): string => {
|
||||
if (!isString(comment)) {
|
||||
throw new Error('Incorrect or missing comment');
|
||||
}
|
||||
|
||||
return comment;
|
||||
};
|
||||
|
||||
const isDate = (date: string): boolean => {
|
||||
return Boolean(Date.parse(date));
|
||||
};
|
||||
|
||||
const parseDate = (date: unknown): string => {
|
||||
if (!isString(date) || !isDate(date)) {
|
||||
throw new Error('Incorrect date: ' + date);
|
||||
}
|
||||
return date;
|
||||
};
|
||||
|
||||
const isWeather = (param: string): param is Weather => {
|
||||
return Object.values(Weather).map(v => v.toString()).includes(param);
|
||||
};
|
||||
|
||||
const parseWeather = (weather: unknown): Weather => {
|
||||
if (!isString(weather) || !isWeather(weather)) {
|
||||
throw new Error('Incorrect weather: ' + weather);
|
||||
}
|
||||
return weather;
|
||||
};
|
||||
|
||||
const isVisibility = (param: string): param is Visibility => {
|
||||
return Object.values(Visibility).map(v => v.toString()).includes(param);
|
||||
};
|
||||
|
||||
const parseVisibility = (visibility: unknown): Visibility => {
|
||||
if (!isString(visibility) || !isVisibility(visibility)) {
|
||||
throw new Error('Incorrect visibility: ' + visibility);
|
||||
}
|
||||
return visibility;
|
||||
};
|
||||
|
||||
const toNewDiaryEntry = (object: unknown): NewDiaryEntry => {
|
||||
if ( !object || typeof object !== 'object' ) {
|
||||
throw new Error('Incorrect or missing data');
|
||||
}
|
||||
|
||||
if ('comment' in object && 'date' in object && 'weather' in object && 'visibility' in object) {
|
||||
const newEntry: NewDiaryEntry = {
|
||||
weather: parseWeather(object.weather),
|
||||
visibility: parseVisibility(object.visibility),
|
||||
date: parseDate(object.date),
|
||||
comment: parseComment(object.comment)
|
||||
};
|
||||
|
||||
return newEntry;
|
||||
}
|
||||
|
||||
throw new Error('Incorrect data: a field missing');
|
||||
};
|
||||
|
||||
export default toNewDiaryEntry;
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES6",
|
||||
"outDir": "./build/",
|
||||
"module": "commonjs",
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"esModuleInterop": true,
|
||||
"resolveJsonModule": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import axios from "axios";
|
||||
|
||||
interface Diary {
|
||||
date: string;
|
||||
visibility: string;
|
||||
weather: string;
|
||||
id: number;
|
||||
comment: string;
|
||||
}
|
||||
|
||||
function App() {
|
||||
const [date, setDate] = useState("");
|
||||
const [visibility, setVisibility] = useState("");
|
||||
const [weather, setWeather] = useState("");
|
||||
const [comment, setComment] = useState("");
|
||||
const addDiary = async (e: React.SyntheticEvent) => {
|
||||
e.preventDefault();
|
||||
const newDiaryEntry: Diary = {
|
||||
date,
|
||||
visibility,
|
||||
weather,
|
||||
id: diaries.length,
|
||||
comment,
|
||||
};
|
||||
try {
|
||||
const resp = await axios.post<Diary>(
|
||||
"http://localhost:3003/api/diaries",
|
||||
newDiaryEntry
|
||||
);
|
||||
setDiaries([...diaries, resp.data]);
|
||||
} catch (error) {
|
||||
if (axios.isAxiosError(error)) {
|
||||
window.alert(error.response?.data);
|
||||
} else {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
setDate("");
|
||||
setVisibility("");
|
||||
setWeather("");
|
||||
setComment("");
|
||||
};
|
||||
const [diaries, setDiaries] = useState<Omit<Diary, "comment">[]>([]);
|
||||
useEffect(() => {
|
||||
axios
|
||||
.get<Omit<Diary, "comment">[]>("http://localhost:3003/api/diaries")
|
||||
.then((resp) => setDiaries(resp.data))
|
||||
.catch((err) => console.log(err.message));
|
||||
}, []);
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<h3>add diary</h3>
|
||||
<form onSubmit={addDiary}>
|
||||
<div>
|
||||
date
|
||||
<input
|
||||
type="date"
|
||||
value={date}
|
||||
onChange={(e) => setDate(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label>visibility: </label>
|
||||
<label>
|
||||
great
|
||||
<input
|
||||
value={visibility}
|
||||
type="radio"
|
||||
onChange={() => setVisibility("great")}
|
||||
name="visibility"
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
good
|
||||
<input
|
||||
value={visibility}
|
||||
type="radio"
|
||||
onChange={() => setVisibility("good")}
|
||||
name="visibility"
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
ok
|
||||
<input
|
||||
value={visibility}
|
||||
type="radio"
|
||||
onChange={() => setVisibility("ok")}
|
||||
name="visibility"
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
poor
|
||||
<input
|
||||
value={visibility}
|
||||
type="radio"
|
||||
onChange={() => setVisibility("poor")}
|
||||
name="visibility"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>weather: </label>
|
||||
<label>
|
||||
sunny
|
||||
<input
|
||||
value={weather}
|
||||
onChange={() => setWeather("sunny")}
|
||||
type="radio"
|
||||
name="weather"
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
rainy
|
||||
<input
|
||||
value={weather}
|
||||
onChange={() => setWeather("rainy")}
|
||||
type="radio"
|
||||
name="weather"
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
cloudy
|
||||
<input
|
||||
value={weather}
|
||||
onChange={() => setWeather("cloudy")}
|
||||
type="radio"
|
||||
name="weather"
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
stormy
|
||||
<input
|
||||
value={weather}
|
||||
onChange={() => setWeather("stormy")}
|
||||
type="radio"
|
||||
name="weather"
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
windy
|
||||
<input
|
||||
value={weather}
|
||||
onChange={() => setWeather("windy")}
|
||||
type="radio"
|
||||
name="weather"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
comment
|
||||
<input
|
||||
value={comment}
|
||||
onChange={(e) => setComment(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<button>add</button>
|
||||
</form>
|
||||
</div>
|
||||
<h3>diray entries</h3>
|
||||
{diaries ? (
|
||||
<ul>
|
||||
{diaries.map((el) => (
|
||||
<li key={el.id}>
|
||||
{el.date} visibility: {el.visibility} weather: {el.weather}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
@@ -0,0 +1,7 @@
|
||||
import ReactDOM from "react-dom/client";
|
||||
import App from "./App";
|
||||
|
||||
const root = ReactDOM.createRoot(
|
||||
document.getElementById("root") as HTMLElement
|
||||
);
|
||||
root.render(<App />);
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user