Skip to content

Commit c9532d4

Browse files
committed
Use path parameters for key and active view
1 parent 1ec7c22 commit c9532d4

File tree

4 files changed

+62
-17
lines changed

4 files changed

+62
-17
lines changed

back-end/feedback/views.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,14 +75,17 @@ def question_answers(request: HttpRequest, key: str):
7575

7676
@_feedback_error_as_json
7777
def answer(request: HttpRequest, id_: str):
78-
if request.method != "PATCH":
78+
if request.method not in ["GET", "PATCH"]:
7979
raise FeedbackError("Method not allowed", 405)
8080

8181
try:
8282
a = Answer.objects.get(id=id_)
8383
except Answer.DoesNotExist:
8484
raise FeedbackError("Answer not found", 404)
8585

86+
if request.method == "GET":
87+
return JsonResponse(a.json)
88+
8689
data = _parse_json_body(request)
8790
if data.pop("submit", None):
8891
a.submitted_at = datetime.now(timezone.utc)

front-end/default.conf.template

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ server {
66

77
location / {
88
root /usr/share/nginx/html;
9-
index index.html index.htm;
9+
try_files $uri $uri/ /index.html /index.htm;
1010
}
1111

1212
location /static/ {

front-end/src/App.svelte

Lines changed: 52 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import RadioGroup from "./lib/RadioGroup.svelte";
66
import {
77
createAnswer,
8+
getAnswer,
89
getQuestion,
910
getSummary,
1011
updateAnswer,
@@ -23,6 +24,7 @@
2324
import Footer from "./lib/Footer.svelte";
2425
2526
let loading = $state(true);
27+
let view = $state<string>("form");
2628
let error = $state<Problem | null>(null);
2729
let question = $state<Question | null>(null);
2830
let answer = $state<Answer | null>(null);
@@ -34,9 +36,15 @@
3436
// Wait until the server has started.
3537
await waitUntilLive();
3638
37-
// Parse the question key from URL query parameters.
39+
// Parse the question key from URL path or URL query parameters.
40+
const pathComponents = window.location.pathname.split("/").slice(1);
3841
const query = new URLSearchParams(window.location.search);
39-
const key = query.get("key");
42+
43+
const key = pathComponents[0] || query.get("key");
44+
// Replace path if query parameter is used. This is for backwards compatibility.
45+
if (key && !pathComponents[0]) {
46+
window.history.replaceState({}, "", `/${key}`);
47+
}
4048
if (!key) {
4149
error = {
4250
status: 404,
@@ -45,6 +53,15 @@
4553
return;
4654
}
4755
56+
view = pathComponents[1] || "form";
57+
if (["form", "summary"].includes(view) === false) {
58+
error = {
59+
status: 404,
60+
title: "Page not found",
61+
};
62+
return;
63+
}
64+
4865
// Fetch the question using the key.
4966
const qr = await getQuestion(key);
5067
if (qr.error) {
@@ -53,13 +70,22 @@
5370
}
5471
question = qr.data;
5572
56-
// Initialize the answer.
57-
const ar = await createAnswer(key);
58-
if (ar.error) {
59-
error = ar.error;
60-
return;
73+
// Initialize new answer or get existings answer.
74+
const id = query.get("id");
75+
if (view === "form") {
76+
const ar = id ? await getAnswer(id) : await createAnswer(key);
77+
if (ar.error) {
78+
error = ar.error;
79+
return;
80+
}
81+
answer = ar.data;
82+
window.history.replaceState({}, "", `/${key}?id=${answer.id}`);
83+
}
84+
85+
// Fetch the summary.
86+
if (view === "summary") {
87+
fetchSummary(key);
6188
}
62-
answer = ar.data;
6389
} catch (err) {
6490
error = {
6591
status: 500,
@@ -70,6 +96,10 @@
7096
}
7197
};
7298
99+
window.addEventListener("popstate", (event) => {
100+
initAnswer();
101+
});
102+
73103
initAnswer();
74104
});
75105
@@ -79,7 +109,7 @@
79109
}
80110
81111
try {
82-
const ar = await updateAnswer(answer?.key, answer?.id, payload);
112+
const ar = await updateAnswer(answer?.id, payload);
83113
if (ar.error) {
84114
error = ar.error;
85115
return;
@@ -93,11 +123,10 @@
93123
}
94124
};
95125
96-
const handleSubmit = async () => {
126+
const fetchSummary = async (key?: string) => {
97127
loading = true;
98-
await handleChange({ submit: true });
99128
try {
100-
const sr = await getSummary(answer?.key ?? "");
129+
const sr = await getSummary(key ?? "");
101130
if (sr.error) {
102131
error = sr.error;
103132
return;
@@ -112,6 +141,15 @@
112141
loading = false;
113142
}
114143
};
144+
145+
const handleSubmit = async () => {
146+
loading = true;
147+
await handleChange({ submit: true });
148+
view = "summary";
149+
window.history.pushState({id: answer?.id}, "", `/${answer?.key}/summary`);
150+
await fetchSummary(answer?.key);
151+
loading = false;
152+
};
115153
</script>
116154

117155
<header>
@@ -125,7 +163,7 @@
125163
<Loading />
126164
{:else if error}
127165
<Error {error} />
128-
{:else if question && !answer?.submitted_at}
166+
{:else if question && view === "form"}
129167
<fieldset>
130168
<legend>{question.choice_text}</legend>
131169
<RadioGroup
@@ -145,7 +183,7 @@
145183
{/if}
146184
<Submit onSubmit={handleSubmit} />
147185
{/if}
148-
{:else if question && answer?.submitted_at && summary}
186+
{:else if question && view === "summary" && summary}
149187
<div class="summary">
150188
<p>Thank you for your feedback!</p>
151189
<BarChart choices={question.choices} {summary} />

front-end/src/lib/api.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,11 @@ export const getQuestion = async (
6868
return buildResponse<Question>(response);
6969
};
7070

71+
export const getAnswer = async (id: string): Promise<APIResponse<Answer>> => {
72+
const response = await fetch(`${baseUrl()}/answer/${id}`);
73+
return buildResponse<Answer>(response);
74+
};
75+
7176
export const createAnswer = async (
7277
key: string,
7378
): Promise<APIResponse<Answer>> => {
@@ -82,7 +87,6 @@ export const createAnswer = async (
8287
};
8388

8489
export const updateAnswer = async (
85-
key: string,
8690
id: string,
8791
payload: UpdateAnswerPayload,
8892
): Promise<APIResponse<Answer>> => {

0 commit comments

Comments
 (0)