Monetization::Issue -> Monetization::Task
This commit is contained in:
parent
40b4553dc3
commit
ccff99fb94
32 changed files with 254 additions and 254 deletions
|
@ -42,5 +42,5 @@ class Twenty::Model < ActiveRecord::Base
|
||||||
connect
|
connect
|
||||||
Twenty::Migration.run!
|
Twenty::Migration.run!
|
||||||
require_relative "model/connection"
|
require_relative "model/connection"
|
||||||
require_relative "model/issue"
|
require_relative "model/task"
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,7 +8,7 @@ class Twenty::Connection < Twenty::Model
|
||||||
|
|
||||||
##
|
##
|
||||||
# Associations
|
# Associations
|
||||||
has_many :issues, class_name: 'Twenty::Issue'
|
has_many :tasks, class_name: 'Twenty::Task'
|
||||||
|
|
||||||
def to_json(options = {})
|
def to_json(options = {})
|
||||||
{id:, name:, path:}.to_json(options)
|
{id:, name:, path:}.to_json(options)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
class Twenty::Issue < Twenty::Model
|
class Twenty::Task < Twenty::Model
|
||||||
self.table_name = 'issues'
|
self.table_name = 'issues'
|
||||||
|
|
||||||
##
|
##
|
|
@ -1,7 +1,7 @@
|
||||||
class Twenty::Servlet < WEBrick::HTTPServlet::AbstractServlet
|
class Twenty::Servlet < WEBrick::HTTPServlet::AbstractServlet
|
||||||
require_relative "servlet/response"
|
require_relative "servlet/response"
|
||||||
require_relative "servlet/connections"
|
require_relative "servlet/connections"
|
||||||
require_relative "servlet/issues"
|
require_relative "servlet/tasks"
|
||||||
|
|
||||||
def ok(res, body = {})
|
def ok(res, body = {})
|
||||||
Response.new(res)
|
Response.new(res)
|
||||||
|
|
|
@ -1,60 +0,0 @@
|
||||||
class Twenty::Servlet::Issues < Twenty::Servlet
|
|
||||||
##
|
|
||||||
# GET /servlet/issues/
|
|
||||||
# GET /servlet/issues/<id>/
|
|
||||||
def do_GET(req, res)
|
|
||||||
case req.path_info
|
|
||||||
when ""
|
|
||||||
issues = Twenty::Issue.open.order(updated_at: :desc)
|
|
||||||
ok(res, issues:)
|
|
||||||
when %r|^/([\d]+)/?$|
|
|
||||||
issue = Twenty::Issue.find_by(id: $1)
|
|
||||||
issue ? ok(res, issue:) : not_found(res)
|
|
||||||
else
|
|
||||||
not_found(res)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# POST /servlet/issues/
|
|
||||||
def do_POST(req, res)
|
|
||||||
case req.path_info
|
|
||||||
when ""
|
|
||||||
issue = Twenty::Issue.new(JSON.parse(req.body))
|
|
||||||
if issue.save
|
|
||||||
ok(res, issue:)
|
|
||||||
else
|
|
||||||
errors = issue.errors.full_messages
|
|
||||||
bad_request(res, errors:)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
not_found(res)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# PUT /servlet/issues
|
|
||||||
def do_PUT(req, res)
|
|
||||||
case req.path_info
|
|
||||||
when ""
|
|
||||||
body = JSON.parse(req.body)
|
|
||||||
id = body.delete("id")
|
|
||||||
issue = Twenty::Issue.find_by(id:)
|
|
||||||
issue.update(body) ? ok(res, issue:) : not_found(res)
|
|
||||||
else
|
|
||||||
not_found(res)
|
|
||||||
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)
|
|
||||||
issue.destroy ? ok(res) : not_found(res)
|
|
||||||
else
|
|
||||||
not_found(res)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
60
twenty-backend/lib/twenty-backend/servlet/tasks.rb
Normal file
60
twenty-backend/lib/twenty-backend/servlet/tasks.rb
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
class Twenty::Servlet::Tasks < Twenty::Servlet
|
||||||
|
##
|
||||||
|
# GET /servlet/tasks/
|
||||||
|
# GET /servlet/tasks/<id>/
|
||||||
|
def do_GET(req, res)
|
||||||
|
case req.path_info
|
||||||
|
when ""
|
||||||
|
tasks = Twenty::Task.open.order(updated_at: :desc)
|
||||||
|
ok(res, tasks:)
|
||||||
|
when %r|^/([\d]+)/?$|
|
||||||
|
task = Twenty::Task.find_by(id: $1)
|
||||||
|
task ? ok(res, task:) : not_found(res)
|
||||||
|
else
|
||||||
|
not_found(res)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# POST /servlet/tasks/
|
||||||
|
def do_POST(req, res)
|
||||||
|
case req.path_info
|
||||||
|
when ""
|
||||||
|
task = Twenty::Task.new(JSON.parse(req.body))
|
||||||
|
if task.save
|
||||||
|
ok(res, task:)
|
||||||
|
else
|
||||||
|
errors = task.errors.full_messages
|
||||||
|
bad_request(res, errors:)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
not_found(res)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# PUT /servlet/tasks
|
||||||
|
def do_PUT(req, res)
|
||||||
|
case req.path_info
|
||||||
|
when ""
|
||||||
|
body = JSON.parse(req.body)
|
||||||
|
id = body.delete("id")
|
||||||
|
task = Twenty::Task.find_by(id:)
|
||||||
|
task.update(body) ? ok(res, task:) : not_found(res)
|
||||||
|
else
|
||||||
|
not_found(res)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# DELETE /servlet/tasks/<id>/
|
||||||
|
def do_DELETE(req, res)
|
||||||
|
case req.path_info
|
||||||
|
when %r|^/([\d]+)/?$|
|
||||||
|
task = Twenty::Task.find_by(id: $1)
|
||||||
|
task.destroy ? ok(res) : not_found(res)
|
||||||
|
else
|
||||||
|
not_found(res)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -12,7 +12,7 @@ class Twenty::Command::Up < Twenty::Command
|
||||||
def run_command
|
def run_command
|
||||||
server = WEBrick::HTTPServer.new(server_options)
|
server = WEBrick::HTTPServer.new(server_options)
|
||||||
server.mount '/servlet/connections', Twenty::Servlet::Connections
|
server.mount '/servlet/connections', Twenty::Servlet::Connections
|
||||||
server.mount '/servlet/issues', Twenty::Servlet::Issues
|
server.mount '/servlet/tasks', Twenty::Servlet::Tasks
|
||||||
trap(:SIGINT) { server.shutdown }
|
trap(:SIGINT) { server.shutdown }
|
||||||
server.start
|
server.start
|
||||||
end
|
end
|
||||||
|
|
|
@ -19,7 +19,7 @@ end
|
||||||
|
|
||||||
require_rules "nanoc/rules/assets"
|
require_rules "nanoc/rules/assets"
|
||||||
require_rules "nanoc/rules/connections"
|
require_rules "nanoc/rules/connections"
|
||||||
require_rules "nanoc/rules/issues"
|
require_rules "nanoc/rules/tasks"
|
||||||
|
|
||||||
compile("/**/*") { write(nil) }
|
compile("/**/*") { write(nil) }
|
||||||
layout '/**/*', :erb
|
layout '/**/*', :erb
|
||||||
|
|
|
@ -1,48 +0,0 @@
|
||||||
#!/usr/bin/env ruby
|
|
||||||
|
|
||||||
##
|
|
||||||
# GET /issues/new/
|
|
||||||
compile '/html/issues/new/index.html.erb' do
|
|
||||||
layout "/default.*"
|
|
||||||
filter(:erb)
|
|
||||||
write("/issues/new/index.html")
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# GET /js/main/issue/new.js
|
|
||||||
compile("/js/main/issue/new.tsx") do
|
|
||||||
filter(:webpack, depend_on: %w[/js/components/ /js/hooks/ /js/types/])
|
|
||||||
write("/js/main/issue/new.js")
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# GET /
|
|
||||||
# GET /issues/
|
|
||||||
compile("/html/issues/index.html.erb") do
|
|
||||||
layout "/default.*"
|
|
||||||
filter(:erb)
|
|
||||||
write("/issues/index.html")
|
|
||||||
write("/index.html")
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# GET /js/main/issues.js
|
|
||||||
compile("/js/main/issues.tsx") do
|
|
||||||
filter(:webpack, depend_on: %w[/js/components /js/hooks/ /js/types/])
|
|
||||||
write("/js/main/issues.js")
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# GET /issues/edit#id=X
|
|
||||||
compile("/html/issues/edit/index.html.erb") do
|
|
||||||
layout "/default.*"
|
|
||||||
filter(:erb)
|
|
||||||
write("/issues/edit/index.html")
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# GET /js/main/issue/edit.js
|
|
||||||
compile("/js/main/issue/edit.tsx") do
|
|
||||||
filter(:webpack, depend_on: %w[/js/components /js/hooks/ /js/types/])
|
|
||||||
write("/js/main/issue/edit.js")
|
|
||||||
end
|
|
48
twenty-frontend/nanoc/rules/tasks.rules
Normal file
48
twenty-frontend/nanoc/rules/tasks.rules
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
#!/usr/bin/env ruby
|
||||||
|
|
||||||
|
##
|
||||||
|
# GET /tasks/new/
|
||||||
|
compile '/html/tasks/new/index.html.erb' do
|
||||||
|
layout "/default.*"
|
||||||
|
filter(:erb)
|
||||||
|
write("/tasks/new/index.html")
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# GET /js/main/task/new.js
|
||||||
|
compile("/js/main/task/new.tsx") do
|
||||||
|
filter(:webpack, depend_on: %w[/js/components/ /js/hooks/ /js/types/])
|
||||||
|
write("/js/main/task/new.js")
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# GET /
|
||||||
|
# GET /tasks/
|
||||||
|
compile("/html/tasks/index.html.erb") do
|
||||||
|
layout "/default.*"
|
||||||
|
filter(:erb)
|
||||||
|
write("/tasks/index.html")
|
||||||
|
write("/index.html")
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# GET /js/main/tasks.js
|
||||||
|
compile("/js/main/tasks.tsx") do
|
||||||
|
filter(:webpack, depend_on: %w[/js/components /js/hooks/ /js/types/])
|
||||||
|
write("/js/main/tasks.js")
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# GET /tasks/edit#id=X
|
||||||
|
compile("/html/tasks/edit/index.html.erb") do
|
||||||
|
layout "/default.*"
|
||||||
|
filter(:erb)
|
||||||
|
write("/tasks/edit/index.html")
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# GET /js/main/task/edit.js
|
||||||
|
compile("/js/main/task/edit.tsx") do
|
||||||
|
filter(:webpack, depend_on: %w[/js/components /js/hooks/ /js/types/])
|
||||||
|
write("/js/main/task/edit.js")
|
||||||
|
end
|
|
@ -36,7 +36,7 @@ body header {
|
||||||
|
|
||||||
.root {
|
.root {
|
||||||
color: $black;
|
color: $black;
|
||||||
.issue {
|
.task {
|
||||||
.content {
|
.content {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
<div class="root align-center max-width">
|
|
||||||
<div class="react-mount edit-issue"></div>
|
|
||||||
<script src="/js/main/issue/edit.js"></script>
|
|
||||||
</div>
|
|
|
@ -1,4 +0,0 @@
|
||||||
<div class="root align-center max-width">
|
|
||||||
<div class="react-mount issues"></div>
|
|
||||||
<script src="/js/main/issues.js"></script>
|
|
||||||
</div>
|
|
|
@ -1,4 +0,0 @@
|
||||||
<div class="root align-center max-width">
|
|
||||||
<div class="react-mount new-issue"></div>
|
|
||||||
<script src="/js/main/issue/new.js"></script>
|
|
||||||
</div>
|
|
4
twenty-frontend/src/html/tasks/edit/index.html.erb
Normal file
4
twenty-frontend/src/html/tasks/edit/index.html.erb
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<div class="root align-center max-width">
|
||||||
|
<div class="react-mount edit-task"></div>
|
||||||
|
<script src="/js/main/task/edit.js"></script>
|
||||||
|
</div>
|
4
twenty-frontend/src/html/tasks/index.html.erb
Normal file
4
twenty-frontend/src/html/tasks/index.html.erb
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<div class="root align-center max-width">
|
||||||
|
<div class="react-mount tasks"></div>
|
||||||
|
<script src="/js/main/tasks.js"></script>
|
||||||
|
</div>
|
4
twenty-frontend/src/html/tasks/new/index.html.erb
Normal file
4
twenty-frontend/src/html/tasks/new/index.html.erb
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<div class="root align-center max-width">
|
||||||
|
<div class="react-mount new-task"></div>
|
||||||
|
<script src="/js/main/task/new.js"></script>
|
||||||
|
</div>
|
|
@ -1,58 +0,0 @@
|
||||||
import React from "react";
|
|
||||||
import { useIssues } from "/hooks/useIssues";
|
|
||||||
import { useDestroyIssue } from "/hooks/useDestroyIssue";
|
|
||||||
import { TrashIcon, DoneIcon } from "/components/Icons";
|
|
||||||
import { DateTime } from "luxon";
|
|
||||||
import { Issue } from "/types/schema";
|
|
||||||
import { useUpsertIssue } from "hooks/useUpsertIssue";
|
|
||||||
|
|
||||||
export function Issues() {
|
|
||||||
const { issues, setIssues } = useIssues();
|
|
||||||
const upsertIssue = useUpsertIssue();
|
|
||||||
const destroyIssue = useDestroyIssue();
|
|
||||||
const onDestroy = (issue: Issue) => {
|
|
||||||
destroyIssue({id: issue.id})
|
|
||||||
.then(() => issues.filter((i) => i.id !== issue.id))
|
|
||||||
.then((issues) => setIssues(issues));
|
|
||||||
}
|
|
||||||
const onDone = (issue: Issue) => {
|
|
||||||
upsertIssue({input: {id: issue.id, state: "closed"}})
|
|
||||||
.then(() => issues.filter((i) => i.id !== issue.id))
|
|
||||||
.then((issues) => setIssues(issues));
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<div className="table">
|
|
||||||
<div className="table div">
|
|
||||||
<span>Tasks</span>
|
|
||||||
<a href="/issues/new">New task</a>
|
|
||||||
</div>
|
|
||||||
<div className="table content">
|
|
||||||
<ul className="items">
|
|
||||||
{issues.map((issue: Issue, key: number) => {
|
|
||||||
const { updated_at: updatedAt } = issue;
|
|
||||||
const datetime = DateTime.fromISO(updatedAt);
|
|
||||||
return (
|
|
||||||
<li className="item" key={key}>
|
|
||||||
<div className="top">
|
|
||||||
<a href={`/issues/edit#id=${issue.id}`}>
|
|
||||||
<span className="item title">{issue.title}</span>
|
|
||||||
</a>
|
|
||||||
<div className="actions">
|
|
||||||
<DoneIcon onClick={() => onDone(issue)} />
|
|
||||||
<TrashIcon onClick={() => onDestroy(issue)}/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="bottom">
|
|
||||||
<span>
|
|
||||||
{datetime.toFormat("dd LLL, yyyy")} at{" "}
|
|
||||||
{datetime.toFormat("HH:mm")}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,9 +1,9 @@
|
||||||
import React, { useEffect, useState, useRef } from "react";
|
import React, { useEffect, useState, useRef } from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { Select } from "/components/forms/Select";
|
import { Select } from "/components/forms/Select";
|
||||||
import { useUpsertIssue } from "/hooks/useUpsertIssue";
|
import { useUpsertTask } from "/hooks/useUpsertTask";
|
||||||
import { useConnections } from "/hooks/useConnections";
|
import { useConnections } from "/hooks/useConnections";
|
||||||
import { Issue } from "/types/schema";
|
import { Task } from "/types/schema";
|
||||||
import showdown from "showdown";
|
import showdown from "showdown";
|
||||||
|
|
||||||
type Inputs = {
|
type Inputs = {
|
||||||
|
@ -13,17 +13,17 @@ type Inputs = {
|
||||||
connectionId: number;
|
connectionId: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function Issue({ issue }: { issue?: Issue }) {
|
export function Task({ task }: { task?: Task }) {
|
||||||
const { register, handleSubmit, watch, setValue: set } = useForm<Inputs>();
|
const { register, handleSubmit, watch, setValue: set } = useForm<Inputs>();
|
||||||
const [isEditable, setIsEditable] = useState<boolean>(!issue);
|
const [isEditable, setIsEditable] = useState<boolean>(!task);
|
||||||
const selectRef = useRef<HTMLSelectElement>(null);
|
const selectRef = useRef<HTMLSelectElement>(null);
|
||||||
const upsert = useUpsertIssue();
|
const upsert = useUpsertTask();
|
||||||
const [connections] = useConnections();
|
const [connections] = useConnections();
|
||||||
const c = new showdown.Converter();
|
const c = new showdown.Converter();
|
||||||
const content = watch("content");
|
const content = watch("content");
|
||||||
const onSave = (input: Inputs) => {
|
const onSave = (input: Inputs) => {
|
||||||
upsert({ input }).then(() => {
|
upsert({ input }).then(() => {
|
||||||
location.href = "/issues/";
|
location.href = "/tasks/";
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -32,8 +32,8 @@ export function Issue({ issue }: { issue?: Issue }) {
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form className="issue" onSubmit={handleSubmit(onSave)}>
|
<form className="task" onSubmit={handleSubmit(onSave)}>
|
||||||
<input type="hidden" value={issue?.id} {...register("id")} />
|
<input type="hidden" value={task?.id} {...register("id")} />
|
||||||
<div className="table">
|
<div className="table">
|
||||||
<div className="table tabbed div">
|
<div className="table tabbed div">
|
||||||
<ul className="tabs">
|
<ul className="tabs">
|
||||||
|
@ -62,7 +62,7 @@ export function Issue({ issue }: { issue?: Issue }) {
|
||||||
className="form"
|
className="form"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Title"
|
placeholder="Title"
|
||||||
defaultValue={issue?.title}
|
defaultValue={task?.title}
|
||||||
{...register("title", { required: true })}
|
{...register("title", { required: true })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -72,7 +72,7 @@ export function Issue({ issue }: { issue?: Issue }) {
|
||||||
<textarea
|
<textarea
|
||||||
className="form"
|
className="form"
|
||||||
placeholder="Add your description heren"
|
placeholder="Add your description heren"
|
||||||
defaultValue={issue?.content}
|
defaultValue={task?.content}
|
||||||
{...register("content", { required: true })}
|
{...register("content", { required: true })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -82,9 +82,9 @@ export function Issue({ issue }: { issue?: Issue }) {
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<div
|
<div
|
||||||
className="issue content"
|
className="task content"
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: c.makeHtml(content || issue?.content),
|
__html: c.makeHtml(content || task?.content),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
58
twenty-frontend/src/js/components/Tasks.tsx
Normal file
58
twenty-frontend/src/js/components/Tasks.tsx
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
import React from "react";
|
||||||
|
import { useTasks } from "/hooks/useTasks";
|
||||||
|
import { useDestroyTask } from "/hooks/useDestroyTask";
|
||||||
|
import { TrashIcon, DoneIcon } from "/components/Icons";
|
||||||
|
import { DateTime } from "luxon";
|
||||||
|
import { Task } from "/types/schema";
|
||||||
|
import { useUpsertTask } from "/hooks/useUpsertTask";
|
||||||
|
|
||||||
|
export function Tasks() {
|
||||||
|
const { tasks, setTasks } = useTasks();
|
||||||
|
const upsertTask = useUpsertTask();
|
||||||
|
const destroyTask = useDestroyTask();
|
||||||
|
const onDestroy = (task: Task) => {
|
||||||
|
destroyTask({id: task.id})
|
||||||
|
.then(() => tasks.filter((t: Task) => t.id !== task.id))
|
||||||
|
.then((tasks: Task[]) => setTasks(tasks));
|
||||||
|
}
|
||||||
|
const onDone = (task: Task) => {
|
||||||
|
upsertTask({input: {id: task.id, state: "closed"}})
|
||||||
|
.then(() => tasks.filter((t: Task) => t.id !== task.id))
|
||||||
|
.then((tasks) => setTasks(tasks));
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className="table">
|
||||||
|
<div className="table div">
|
||||||
|
<span>Tasks</span>
|
||||||
|
<a href="/tasks/new">New task</a>
|
||||||
|
</div>
|
||||||
|
<div className="table content">
|
||||||
|
<ul className="items">
|
||||||
|
{tasks.map((task: Task, key: number) => {
|
||||||
|
const { updated_at: updatedAt } = task;
|
||||||
|
const datetime = DateTime.fromISO(updatedAt);
|
||||||
|
return (
|
||||||
|
<li className="item" key={key}>
|
||||||
|
<div className="top">
|
||||||
|
<a href={`/tasks/edit#id=${task.id}`}>
|
||||||
|
<span className="item title">{task.title}</span>
|
||||||
|
</a>
|
||||||
|
<div className="actions">
|
||||||
|
<DoneIcon onClick={() => onDone(task)} />
|
||||||
|
<TrashIcon onClick={() => onDestroy(task)}/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="bottom">
|
||||||
|
<span>
|
||||||
|
{datetime.toFormat("dd LLL, yyyy")} at{" "}
|
||||||
|
{datetime.toFormat("HH:mm")}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -2,11 +2,11 @@ type Params = {
|
||||||
id: number;
|
id: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function useDestroyIssue() {
|
export function useDestroyTask() {
|
||||||
return function ({ id }: Params) {
|
return function ({ id }: Params) {
|
||||||
return new Promise((accept, reject) => {
|
return new Promise((accept, reject) => {
|
||||||
const req = { method: "DELETE" };
|
const req = { method: "DELETE" };
|
||||||
return fetch(`/servlet/issues/${id}`, req)
|
return fetch(`/servlet/tasks/${id}`, req)
|
||||||
.then(res => res.json())
|
.then(res => res.json())
|
||||||
.then(accept)
|
.then(accept)
|
||||||
.catch(reject);
|
.catch(reject);
|
|
@ -1,27 +0,0 @@
|
||||||
import { useState, useEffect } from "react";
|
|
||||||
import { Issue } from "/types/schema";
|
|
||||||
|
|
||||||
type Result = {
|
|
||||||
setIssues: (issues: Issue[]) => unknown;
|
|
||||||
issues: Issue[];
|
|
||||||
req: () => Promise<Issue[]>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function useIssues(): Result {
|
|
||||||
const [issues, setIssues] = useState<Issue[]>([]);
|
|
||||||
const set = (ary: Issue[]) => {
|
|
||||||
setIssues(ary);
|
|
||||||
return ary;
|
|
||||||
};
|
|
||||||
const req = async function (): Promise<Issue[]> {
|
|
||||||
return await fetch("/servlet/issues")
|
|
||||||
.then((res: Response) => res.json())
|
|
||||||
.then((res: { issues: Issue[] }) => set(res.issues))
|
|
||||||
.catch(() => null);
|
|
||||||
};
|
|
||||||
useEffect(() => {
|
|
||||||
req();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return { issues, setIssues: set, req };
|
|
||||||
}
|
|
27
twenty-frontend/src/js/hooks/useTasks.ts
Normal file
27
twenty-frontend/src/js/hooks/useTasks.ts
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import { useState, useEffect } from "react";
|
||||||
|
import { Task } from "/types/schema";
|
||||||
|
|
||||||
|
type Result = {
|
||||||
|
setTasks: (tasks: Task[]) => unknown;
|
||||||
|
tasks: Task[];
|
||||||
|
req: () => Promise<Task[]>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function useTasks(): Result {
|
||||||
|
const [tasks, setTasks] = useState<Task[]>([]);
|
||||||
|
const set = (ary: Task[]) => {
|
||||||
|
setTasks(ary);
|
||||||
|
return ary;
|
||||||
|
};
|
||||||
|
const req = async function (): Promise<Task[]> {
|
||||||
|
return await fetch("/servlet/tasks")
|
||||||
|
.then((res: Response) => res.json())
|
||||||
|
.then((res: { tasks: Task[] }) => set(res.tasks))
|
||||||
|
.catch(() => null);
|
||||||
|
};
|
||||||
|
useEffect(() => {
|
||||||
|
req();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return { tasks, setTasks: set, req };
|
||||||
|
}
|
|
@ -6,7 +6,7 @@ type Params = {
|
||||||
connectionId?: number;
|
connectionId?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function useUpsertIssue() {
|
export function useUpsertTask() {
|
||||||
const normalize = (input: Params) => {
|
const normalize = (input: Params) => {
|
||||||
const { id, title, content, state, connectionId } = input;
|
const { id, title, content, state, connectionId } = input;
|
||||||
return { id, title, content, state, connection_id: connectionId };
|
return { id, title, content, state, connection_id: connectionId };
|
||||||
|
@ -17,7 +17,7 @@ export function useUpsertIssue() {
|
||||||
method: input.id ? "PUT" : "POST",
|
method: input.id ? "PUT" : "POST",
|
||||||
body: JSON.stringify(normalize(input)),
|
body: JSON.stringify(normalize(input)),
|
||||||
};
|
};
|
||||||
return fetch("/servlet/issues", req)
|
return fetch("/servlet/tasks", req)
|
||||||
.then(res => res.json())
|
.then(res => res.json())
|
||||||
.then(accept)
|
.then(accept)
|
||||||
.catch(reject);
|
.catch(reject);
|
|
@ -1,8 +0,0 @@
|
||||||
import React from "react";
|
|
||||||
import ReactDOM from "react-dom/client";
|
|
||||||
import { ReadIssue } from "/components/ReadIssue";
|
|
||||||
|
|
||||||
(function () {
|
|
||||||
const root = document.querySelector(".react-mount.edit-issue")!;
|
|
||||||
ReactDOM.createRoot(root).render(<ReadIssue />);
|
|
||||||
})();
|
|
|
@ -1,8 +0,0 @@
|
||||||
import React from "react";
|
|
||||||
import ReactDOM from "react-dom/client";
|
|
||||||
import { Issue } from "/components/Issue";
|
|
||||||
|
|
||||||
(function () {
|
|
||||||
const root = document.querySelector(".react-mount.new-issue")!;
|
|
||||||
ReactDOM.createRoot(root).render(<Issue />);
|
|
||||||
})();
|
|
|
@ -1,8 +0,0 @@
|
||||||
import React from "react";
|
|
||||||
import ReactDOM from "react-dom/client";
|
|
||||||
import { Issues } from "/components/Issues";
|
|
||||||
|
|
||||||
(function () {
|
|
||||||
const n1 = document.querySelector(".react-mount.issues")!;
|
|
||||||
ReactDOM.createRoot(n1).render(<Issues />);
|
|
||||||
})();
|
|
8
twenty-frontend/src/js/main/task/edit.tsx
Normal file
8
twenty-frontend/src/js/main/task/edit.tsx
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import React from "react";
|
||||||
|
import ReactDOM from "react-dom/client";
|
||||||
|
import { Task } from "/components/Task";
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
const root = document.querySelector(".react-mount.edit-task")!;
|
||||||
|
ReactDOM.createRoot(root).render(<Task/>);
|
||||||
|
})();
|
8
twenty-frontend/src/js/main/task/new.tsx
Normal file
8
twenty-frontend/src/js/main/task/new.tsx
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import React from "react";
|
||||||
|
import ReactDOM from "react-dom/client";
|
||||||
|
import { Task } from "/components/Task";
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
const root = document.querySelector(".react-mount.new-task")!;
|
||||||
|
ReactDOM.createRoot(root).render(<Task />);
|
||||||
|
})();
|
8
twenty-frontend/src/js/main/tasks.tsx
Normal file
8
twenty-frontend/src/js/main/tasks.tsx
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import React from "react";
|
||||||
|
import ReactDOM from "react-dom/client";
|
||||||
|
import { Tasks } from "/components/Tasks";
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
const n1 = document.querySelector(".react-mount.tasks")!;
|
||||||
|
ReactDOM.createRoot(n1).render(<Tasks />);
|
||||||
|
})();
|
|
@ -4,7 +4,7 @@ export type Connection = {
|
||||||
path: string;
|
path: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Issue = {
|
export type Task = {
|
||||||
id: number;
|
id: number;
|
||||||
title: string;
|
title: string;
|
||||||
content: string;
|
content: string;
|
||||||
|
|
|
@ -8,7 +8,7 @@ Gem::Specification.new do |gem|
|
||||||
gem.version = "0.1.0"
|
gem.version = "0.1.0"
|
||||||
gem.licenses = ["0BSD"]
|
gem.licenses = ["0BSD"]
|
||||||
gem.files = []
|
gem.files = []
|
||||||
gem.summary = "Simple task management for hobby projects"
|
gem.summary = "twenty provides simple task management on http://localhost"
|
||||||
gem.description = gem.summary
|
gem.description = gem.summary
|
||||||
gem.add_runtime_dependency "twenty-backend", "~> 0.1"
|
gem.add_runtime_dependency "twenty-backend", "~> 0.1"
|
||||||
gem.add_runtime_dependency "twenty-frontend", "~> 0.1"
|
gem.add_runtime_dependency "twenty-frontend", "~> 0.1"
|
||||||
|
|
Loading…
Reference in a new issue