Skip to content
Snippets Groups Projects
Commit 578ac5a5 authored by Jaela Field's avatar Jaela Field
Browse files

hw-wedding

parents
Branches master
No related tags found
No related merge requests found
node_modules/*
!node_modules/coZ
.vscode
dist
This diff is collapsed.
{
"name": "cse331-hw-wedding-client",
"version": "0.0.1",
"description": "CSE 331 Wedding",
"main": "",
"scripts": {
"build": "webpack --config src/webpack.config.js",
"lint": "comfy-tslint --no-mutation src/*.{ts,tsx}",
"test": "ts-mocha -p tsconfig.json src/**/*_test.{ts,tsx} --require ignore-styles",
"start": "webpack-dev-server --config src/webpack.config.js"
},
"author": "",
"license": "ISC",
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@types/assert": "^1.5.6",
"@types/mocha": "^5.2.7",
"@types/react": "^18.0.25",
"@types/react-dom": "^18.0.8",
"assert": "^2.0.0",
"clean-webpack-plugin": "^4.0.0",
"comfy-tslint": "^0.1.9",
"copy-webpack-plugin": "^5.0.1",
"css-loader": "^6.7.1",
"file-loader": "^6.2.0",
"html-webpack-plugin": "^5.5.0",
"ignore-styles": "^5.0.1",
"mini-css-extract-plugin": "^2.6.1",
"mocha": "^10.1.0",
"progress-bar-webpack-plugin": "^2.1.0",
"ts-loader": "^9.4.1",
"ts-mocha": "^10.0.0",
"typescript": "^4.8.4",
"webpack": "^5.75.0",
"webpack-cli": "^4.10.0",
"webpack-dev-server": "^4.11.1"
}
}
import React, { Component, ChangeEvent, MouseEvent } from "react";
import { isRecord } from './record';
// TODO: When you're ready to get started, you can remove all the example
// code below and start with this blank application:
// type WeddingAppState = {
// }
//
// /** Displays the UI of the Wedding rsvp application. */
// export class WeddingApp extends Component<{}, WeddingAppState> {
//
// constructor(props: {}) {
// super(props);
//
// this.state = {};
// }
//
// render = (): JSX.Element => {
// return <div></div>;
// };
// }
type WeddingAppState = {
name: string; // mirror state of name text box
msg: string; // message sent from server
}
/** Displays the UI of the Wedding rsvp application. */
export class WeddingApp extends Component<{}, WeddingAppState> {
constructor(props: {}) {
super(props);
this.state = {name: "", msg: ""};
}
render = (): JSX.Element => {
return (<div>
<div>
<label htmlFor="name">Name:</label>
<input type="name" id="name" value={this.state.name}
onChange={this.doNameChange}></input>
<button onClick={this.doDummyClick}>Dummy</button>
</div>
{this.renderMessage()}
</div>);
};
renderMessage = (): JSX.Element => {
if (this.state.msg === "") {
return <div></div>;
} else {
return <p>Server says: {this.state.msg}</p>;
}
};
doNameChange = (evt: ChangeEvent<HTMLInputElement>): void => {
this.setState({name: evt.target.value, msg: ""});
};
doDummyClick = (_evt: MouseEvent<HTMLButtonElement>): void => {
const name = this.state.name.trim();
if (name.length > 0) {
const url = "/api/dummy?name=" + encodeURIComponent(name);
fetch(url).then(this.doDummyResp)
.catch(() => this.doDummyError("failed to connect to server"));
}
};
doDummyResp = (res: Response): void => {
if (res.status === 200) {
res.json().then(this.doDummyJson)
.catch(() => this.doDummyError("200 response is not JSON"));
} else if (res.status === 400) {
res.text().then(this.doDummyError)
.catch(() => this.doDummyError("400 response is not name"));
} else {
this.doDummyError(`bad status code ${res.status}`);
}
};
doDummyJson = (data: unknown): void => {
if (!isRecord(data)) {
console.error("200 response is not a record", data);
return;
}
if (typeof data.msg !== "string") {
console.error("'msg' field of 200 response is not a string", data.msg);
return;
}
this.setState({msg: data.msg});
}
doDummyError = (msg: string): void => {
console.error(`Error fetching /api/dummy: ${msg}`);
};
}
\ No newline at end of file
client/src/img/favicon.ico

3.78 KiB

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title><%= TITLE %></title>
</head>
<body>
<div id="main"></div>
</body>
</html>
import React from 'react';
import { createRoot, Root } from 'react-dom/client';
import { WeddingApp } from './WeddingApp';
const main: HTMLElement|null = document.getElementById('main');
if (main === null)
throw new Error("Uh oh! HTML is missing 'main' element");
const root: Root = createRoot(main);
root.render(<WeddingApp/>);
/**
* Determines whether the given value is a record.
* @param val the value in question
* @return true if the value is a record and false otherwise
*/
export const isRecord = (val: unknown): val is Record<string, unknown> => {
return val !== null && typeof val === "object";
};
\ No newline at end of file
const webpack = require('webpack');
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const ProgressBarPlugin = require('progress-bar-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const info = {
TITLE: 'Wedding'
}
const config = {
mode: 'development',
devtool: 'source-map',
devServer: {
static: path.join(__dirname, '../dist'),
port: 8080,
host: '0.0.0.0',
historyApiFallback: true,
headers: {
'Cache-Control': 'no-store',
},
proxy: {
'/api': {
target: 'http://localhost:8080',
router: () => 'http://localhost:8088',
logLevel: 'debug'
}
}
},
module: {
rules: [
{
test: /\.tsx?$/,
use: {
loader: 'ts-loader',
options: {
experimentalWatchApi: true,
},
},
exclude: /node_modules/,
},
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader'
]
},
{
test: /\.(ico|png)$/i,
use: ['file-loader'],
},
]
},
resolve: {
extensions: [
'.tsx', '.ts', '.js', '.jsx', '.json'
]
},
entry: {
main: './src/index.tsx'
},
output: {
filename: '[name].[chunkhash].js',
path: path.resolve(__dirname, '../dist'),
publicPath: '/'
},
plugins: [
new ProgressBarPlugin({ width: 80 }),
new CleanWebpackPlugin(),
new CopyWebpackPlugin(["src/img/*"]),
new MiniCssExtractPlugin({ filename: '[contenthash].css' }),
new HtmlWebpackPlugin({
filename: 'index.html',
title: info.TITLE,
chunks: ['main'],
template: './src/index.html',
templateParameters: { TITLE: info.TITLE }
}),
],
}
module.exports = [ config ];
{
"compilerOptions": {
"module": "commonjs",
"esModuleInterop": true,
"target": "ES2020",
"moduleResolution": "node",
"outDir": "dist",
"rootDir": "src",
"lib": [ "ES2020", "dom" ],
"allowJs": true,
"jsx": "react",
"strict": true,
"noImplicitAny": true,
"noImplicitThis": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"inlineSourceMap": true,
"downlevelIteration": true
}
}
node_modules/*
!node_modules/coZ
.vscode
dist
This diff is collapsed.
{
"name": "cse331-hw-wedding-server",
"version": "0.0.1",
"description": "CSE 331 Wedding Server",
"main": "dist/index.js",
"scripts": {
"build": "tsc --build tsconfig.json",
"lint": "comfy-tslint src/*.{ts,tsx}",
"test": "ts-mocha -p tsconfig.json src/**/*_test.ts",
"server": "node .",
"start": "tsc-watch -p tsconfig.json --onSuccess \"node .\""
},
"author": "",
"license": "ISC",
"dependencies": {
"body-parser": "^1.20.2",
"express": "^4.18.2"
},
"devDependencies": {
"@types/assert": "^1.5.6",
"@types/body-parser": "^1.19.2",
"@types/express": "^4.17.17",
"@types/mocha": "^5.2.7",
"assert": "^2.0.0",
"comfy-tslint": "^0.1.9",
"mocha": "^10.1.0",
"node-mocks-http": "^1.12.1",
"ts-mocha": "^10.0.0",
"tsc-watch": "^6.0.4",
"typescript": "^4.8.4"
}
}
import express, { Express } from "express";
import { dummy } from './routes';
import bodyParser from 'body-parser';
// Configure and start the HTTP server.
const port: number = 8088;
const app: Express = express();
app.use(bodyParser.json());
app.get("/api/dummy", dummy); // TODO: REMOVE
app.listen(port, () => console.log(`Server listening on ${port}`));
import { Request, Response } from "express";
import { ParamsDictionary } from "express-serve-static-core";
// Require type checking of request body.
type SafeRequest = Request<ParamsDictionary, {}, Record<string, unknown>>;
type SafeResponse = Response; // only writing, so no need to check
// TODO: remove the dummy route
/**
* Dummy route that just returns a hello message to the client.
* @param req The request object
* @param res The response object
*/
export const dummy = (req: SafeRequest, res: SafeResponse): void => {
const name = first(req.query.name);
if (name === undefined) {
res.status(400).send('missing or invalid "name" parameter');
return;
}
res.send({msg: `Hi, ${name}!`});
};
// Helper to return the (first) value of the parameter if any was given.
// (This is mildly annoying because the client can also give mutiple values,
// in which case, express puts them into an array.)
const first = (param: unknown): string|undefined => {
if (Array.isArray(param)) {
return first(param[0]);
} else if (typeof param === 'string') {
return param;
} else {
return undefined;
}
};
import * as assert from 'assert';
import * as httpMocks from 'node-mocks-http';
import { dummy } from './routes';
describe('routes', function() {
// TODO: remove the tests for the dummy route
it('dummy', function() {
const req1 = httpMocks.createRequest(
{method: 'GET', url: '/api/dummy', query: {name: 'Bob'} });
const res1 = httpMocks.createResponse();
dummy(req1, res1);
assert.strictEqual(res1._getStatusCode(), 200);
assert.deepStrictEqual(res1._getData(), {msg: "Hi, Bob!"});
});
});
{
"compilerOptions": {
"module": "commonjs",
"esModuleInterop": true,
"target": "ES2020",
"moduleResolution": "node",
"outDir": "dist",
"rootDir": "src",
"lib": [ "ES2020", "dom" ],
"allowJs": true,
"jsx": "react",
"strict": true,
"noImplicitAny": true,
"noImplicitThis": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"inlineSourceMap": true,
"downlevelIteration": true
},
"include": ["src/**/*"]
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment