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

react wizardry #8

Merged
merged 3 commits into from
Aug 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
33 changes: 19 additions & 14 deletions main.w
Original file line number Diff line number Diff line change
Expand Up @@ -50,24 +50,24 @@ class Util {
class Store {
table: ddb.DynamoDBTable;
init() {
this.table = new ddb.DynamoDBTable(hashKey: "Name") as "Items";
this.table = new ddb.DynamoDBTable(hashKey: "Name") as "Entries";
}

inflight setEntry(entry: Entry) {
this.table.putItem(_entryToMap(entry));
}

inflight getRandomPair(): Array<Entry> {
let items = this.table.scan();
let entries = this.table.scan();

let firstIdx = math.floor(math.random() * items.length);
let var secondIdx = math.floor(math.random() * items.length);
while secondIdx != firstIdx {
secondIdx = math.floor(math.random() * items.length);
let firstIdx = math.floor(math.random() * entries.length);
let var secondIdx = math.floor(math.random() * entries.length);
while secondIdx == firstIdx {
secondIdx = math.floor(math.random() * entries.length);
}

let first = _mapToEntry(items.at(firstIdx));
let second = _mapToEntry(items.at(secondIdx));
let first = _mapToEntry(entries.at(firstIdx));
let second = _mapToEntry(entries.at(secondIdx));
return [first, second];
}

Expand Down Expand Up @@ -101,6 +101,7 @@ class Store {

let store = new Store() as "VotingAppStore";

// about 40 items... any more and we need to start paginating
let foods = [
"Nigiri sushi",
"Pizza margherita",
Expand Down Expand Up @@ -163,7 +164,11 @@ website.addJson("config.json", { apiUrl: api.url });

// Select two random items from the list of items for the user to choose between
api.post("/requestChoices", inflight (_) => {
let items = store.getRandomPair();
let entries = store.getRandomPair();
let entryNames = MutArray<str>[];
for entry in entries {
entryNames.push(entry.name);
}
return cloud.ApiResponse {
// TODO: refactor to a constant - https://github.com/winglang/wing/issues/3119
headers: {
Expand All @@ -172,13 +177,13 @@ api.post("/requestChoices", inflight (_) => {
"Access-Control-Allow-Methods" => "OPTIONS,GET",
},
status: 200,
body: Json.stringify(items),
body: Json.stringify(entryNames),
};
});

// Obtain a list of all items and their scores
api.get("/items", inflight (_) => {
let items = store.list();
// Obtain a list of all entries and their scores
api.get("/leaderboard", inflight (_) => {
let entries = store.list();
return cloud.ApiResponse {
// TODO: refactor to a constant - https://github.com/winglang/wing/issues/3119
headers: {
Expand All @@ -187,7 +192,7 @@ api.get("/items", inflight (_) => {
"Access-Control-Allow-Methods" => "OPTIONS,GET",
},
status: 200,
body: Json.stringify(items),
body: Json.stringify(entries),
};
});

Expand Down
18 changes: 11 additions & 7 deletions website/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions website/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,14 @@
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"@types/jest": "^29.5.3",
"@types/node": "^20.4.8",
"@types/react": "^18.2.18",
"@types/react-dom": "^18.2.7",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"typescript": "^4.9.5",
"web-vitals": "^2.1.4"
},
"scripts": {
Expand Down
56 changes: 0 additions & 56 deletions website/src/App.js

This file was deleted.

File renamed without changes.
119 changes: 119 additions & 0 deletions website/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import React, { useState, useEffect } from "react";

interface Config {
baseUrl: string;
}

interface Entry {
name: string;
score: number;
}

const fetchConfig = async () => {
const response = await fetch("./config.json");
if (!response.ok) {
throw new Error('Failed to fetch config');
}
const config: Config = await response.json();
return config;
}

const fetchItems = async () => {
const baseUrl = (await fetchConfig()).baseUrl;
const response = await fetch(baseUrl + "/items");
if (!response.ok) {
throw new Error('Failed to fetch leaderboard data');
}
const jsonData: Entry[] = await response.json();
return jsonData;
}

const fetchChoices = async () => {
const baseUrl = (await fetchConfig()).baseUrl;
const response = await fetch(baseUrl + "/requestChoices", {
method: "POST",
});
if (!response.ok) {
throw new Error('Failed to request choices');
}
const jsonData: string[] = await response.json();
return jsonData;
}

interface LeaderboardProps {
swapViews: () => void;
}

const Leaderboard = (props: LeaderboardProps) => {
const [data, setData] = useState<Entry[]>([]);
useEffect(() => {
fetchItems().then((items) => setData(items));
}, []);

return (
<div>
<h2>Leaderboard</h2>
<table>
<thead>
<tr>
<th>Name</th>
<th>Score</th>
</tr>
</thead>
<tbody>
{data.sort((a, b) => b.score - a.score).map((item) => (
<tr key={item.name}>
<td>{item.name}</td>
<td>{item.score}</td>
</tr>
))}
</tbody>
</table>
<button onClick={props.swapViews}>Back</button>
</div>
);
};

interface VotingProps {
swapViews: () => void;
}

const Voting = (props: VotingProps) => {
const [choices, setChoices] = useState<string[]>([]);
useEffect(() => {
fetchChoices().then((choices) => setChoices(choices));
}, []);

return (
<div>
<h2>Which is better?</h2>
<div className="choices">
{choices.map((item) => (
<div key={item} className="choice">
<div className="name">{item}</div>
<button className="button">Vote</button>
</div>
))}
</div>
<button onClick={props.swapViews}>Leaderboard</button>
</div>
)
}

type View = "voting" | "leaderboard";

function App() {
let [view, setView] = useState<View>("voting");
const swapViews = () => {
setView(view === "voting" ? "leaderboard" : "voting");
};

switch (view) {
case "voting":
return <Voting swapViews={swapViews} />;
case "leaderboard":
return <Leaderboard swapViews={swapViews} />;
}
}

export default App;
2 changes: 1 addition & 1 deletion website/src/index.js → website/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

const root = ReactDOM.createRoot(document.getElementById('root'));
const root = ReactDOM.createRoot(document.getElementById('root')!);
root.render(
<React.StrictMode>
<App />
Expand Down
26 changes: 26 additions & 0 deletions website/tsconfig.json
Original file line number Diff line number Diff line change
@@ -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"
]
}