frontend/backend: add ability to destroy an issue
This commit is contained in:
parent
8b4387443c
commit
dc4482035d
5 changed files with 74 additions and 16 deletions
|
@ -1,14 +1,15 @@
|
||||||
class Twenty::Servlet::Issues < Twenty::Servlet
|
class Twenty::Servlet::Issues < Twenty::Servlet
|
||||||
|
##
|
||||||
|
# GET /servlet/issues/
|
||||||
|
# GET /servlet/issues/<id>/
|
||||||
def do_GET(req, res)
|
def do_GET(req, res)
|
||||||
case req.path_info
|
case req.path_info
|
||||||
when ""
|
when ""
|
||||||
# GET /servlet/issues/
|
|
||||||
issues = Twenty::Issue.open.order(updated_at: :desc)
|
issues = Twenty::Issue.open.order(updated_at: :desc)
|
||||||
Response.new(res)
|
Response.new(res)
|
||||||
.set_status(200)
|
.set_status(200)
|
||||||
.set_body(issues:)
|
.set_body(issues:)
|
||||||
when %r|^/([\d]+)/?$|
|
when %r|^/([\d]+)/?$|
|
||||||
# GET /servlet/issues/<issue-id>/
|
|
||||||
issue = Twenty::Issue.find_by(id: $1)
|
issue = Twenty::Issue.find_by(id: $1)
|
||||||
if issue
|
if issue
|
||||||
Response.new(res)
|
Response.new(res)
|
||||||
|
@ -22,10 +23,11 @@ class Twenty::Servlet::Issues < Twenty::Servlet
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# POST /servlet/issues/
|
||||||
def do_POST(req, res)
|
def do_POST(req, res)
|
||||||
case req.path_info
|
case req.path_info
|
||||||
when ""
|
when ""
|
||||||
# POST /servlet/issues/
|
|
||||||
issue = Twenty::Issue.new(JSON.parse(req.body))
|
issue = Twenty::Issue.new(JSON.parse(req.body))
|
||||||
if issue.save
|
if issue.save
|
||||||
Response.new(res)
|
Response.new(res)
|
||||||
|
@ -41,4 +43,22 @@ class Twenty::Servlet::Issues < Twenty::Servlet
|
||||||
Response.new(res).not_found
|
Response.new(res).not_found
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# DELETE /servlet/issues/<id>/
|
||||||
|
def do_DELETE(req, res)
|
||||||
|
case req.path_info
|
||||||
|
when %r|^/([\d]+)/?$|
|
||||||
|
issue = Twenty::Issue.find_by(id: $1)
|
||||||
|
if issue.destroy
|
||||||
|
Response.new(res)
|
||||||
|
.set_status(200)
|
||||||
|
.set_body({ok: true})
|
||||||
|
else
|
||||||
|
Response.new(res).not_found
|
||||||
|
end
|
||||||
|
else
|
||||||
|
Response.new(res).not_found
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useIssues } from "/hooks/useIssues";
|
import { useIssues } from "/hooks/useIssues";
|
||||||
|
import { useDestroyIssue } from "/hooks/useDestroyIssue";
|
||||||
import { DateTime } from "luxon";
|
import { DateTime } from "luxon";
|
||||||
|
import { Issue } from "/types/schema";
|
||||||
|
|
||||||
export function Issues() {
|
export function Issues() {
|
||||||
const [issues] = useIssues();
|
const { issues, refetch } = useIssues();
|
||||||
|
const destroyIssue = useDestroyIssue();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="pure-u-1-1 issue-index">
|
<div className="pure-u-1-1 issue-index">
|
||||||
<div className="pure-u-5-5 issue-row">
|
<div className="pure-u-5-5 issue-row">
|
||||||
|
@ -18,7 +22,7 @@ export function Issues() {
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
<hr />
|
||||||
<ul className="pure-u-5-5 issue-items">
|
<ul className="pure-u-5-5 issue-items">
|
||||||
{issues.map((issue, key) => {
|
{issues.map((issue: Issue, key: number) => {
|
||||||
const { updated_at: updatedAt } = issue;
|
const { updated_at: updatedAt } = issue;
|
||||||
const datetime = DateTime.fromISO(updatedAt);
|
const datetime = DateTime.fromISO(updatedAt);
|
||||||
return (
|
return (
|
||||||
|
@ -30,6 +34,17 @@ export function Issues() {
|
||||||
{datetime.toFormat("dd LLL, yyyy")} at{" "}
|
{datetime.toFormat("dd LLL, yyyy")} at{" "}
|
||||||
{datetime.toFormat("HH:mm")}
|
{datetime.toFormat("HH:mm")}
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
onClick={() =>
|
||||||
|
destroyIssue({ id: issue.id }).then(() => {
|
||||||
|
refetch();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Destroy
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
|
@ -54,11 +54,7 @@ export function NewIssue() {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<input
|
<input className="form" type="submit" value="Save" />
|
||||||
className="form"
|
|
||||||
type="submit"
|
|
||||||
value="Save"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
|
|
15
twenty-frontend/src/js/hooks/useDestroyIssue.ts
Normal file
15
twenty-frontend/src/js/hooks/useDestroyIssue.ts
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
type Params = {
|
||||||
|
id: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function useDestroyIssue() {
|
||||||
|
return function ({ id }: Params) {
|
||||||
|
return new Promise((accept, reject) => {
|
||||||
|
const req = { method: "DELETE" };
|
||||||
|
return fetch(`/servlet/issues/${id}`, req)
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(accept)
|
||||||
|
.catch(reject);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,14 +1,26 @@
|
||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import { Issue } from "/types/schema";
|
import { Issue } from "/types/schema";
|
||||||
|
|
||||||
export function useIssues() {
|
type Result = {
|
||||||
const [issues, setIssues] = useState<Issue[]>([]);
|
issues: Issue[];
|
||||||
|
refetch: () => Promise<Issue[]>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function useIssues(): Result {
|
||||||
|
const [issues, setIssues] = useState<Issue[]>([]);
|
||||||
|
const set = (ary: Issue[]) => {
|
||||||
|
setIssues(ary);
|
||||||
|
return ary;
|
||||||
|
};
|
||||||
|
const refetch = async function (): Promise<Issue[]> {
|
||||||
|
return await fetch("/servlet/issues")
|
||||||
|
.then((res: Response) => res.json())
|
||||||
|
.then((res: { issues: Issue[] }) => set(res.issues))
|
||||||
|
.catch(() => null);
|
||||||
|
};
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetch("/servlet/issues")
|
refetch();
|
||||||
.then(res => res.json())
|
|
||||||
.then(res => setIssues(res.issues));
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return [issues];
|
return { issues, refetch };
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue