Skip to content

Commit

Permalink
Merge pull request #8 from winglang/rybickic/website-skeleton
Browse files Browse the repository at this point in the history
  • Loading branch information
Chriscbr authored Aug 8, 2023
2 parents 3fe1206 + d65086e commit fc3ee22
Show file tree
Hide file tree
Showing 8 changed files with 181 additions and 78 deletions.
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"
]
}

0 comments on commit fc3ee22

Please sign in to comment.