diff --git a/twenty-backend/lib/twenty-backend/graphql/input/task_input.rb b/twenty-backend/lib/twenty-backend/graphql/input/task_input.rb index fecbfb1..4b85045 100644 --- a/twenty-backend/lib/twenty-backend/graphql/input/task_input.rb +++ b/twenty-backend/lib/twenty-backend/graphql/input/task_input.rb @@ -1,7 +1,9 @@ module Twenty::GraphQL::Input class TaskInput < GraphQL::Schema::InputObject - argument :title, String - argument :content, String - argument :project_id, Int + require_relative "../type/task_status" + argument :title, String, required: false + argument :content, String, required: false + argument :project_id, Int, required: false + argument :status, Twenty::GraphQL::Type::TaskStatus, required: false end end diff --git a/twenty-backend/lib/twenty-backend/graphql/type/query.rb b/twenty-backend/lib/twenty-backend/graphql/type/query.rb index 07f02fd..610e200 100644 --- a/twenty-backend/lib/twenty-backend/graphql/type/query.rb +++ b/twenty-backend/lib/twenty-backend/graphql/type/query.rb @@ -3,16 +3,18 @@ module Twenty::GraphQL::Type field :find_task, Task, null: true do argument :task_id, Int end - field :tasks, [Task], null: false + field :tasks, [Task], null: false do + argument :status, TaskStatus + end field :projects, [Project], null: false def find_task(task_id:) Twenty::Task.find_by(id: task_id) end - def tasks + def tasks(status:) Twenty::Task - .ready + .where(status:) .order(updated_at: :desc) end diff --git a/twenty-backend/lib/twenty-backend/graphql/type/task.rb b/twenty-backend/lib/twenty-backend/graphql/type/task.rb index bb27040..033ee4b 100644 --- a/twenty-backend/lib/twenty-backend/graphql/type/task.rb +++ b/twenty-backend/lib/twenty-backend/graphql/type/task.rb @@ -7,5 +7,6 @@ module Twenty::GraphQL::Type field :content, String, null: false field :project, Project, null: false field :updated_at, GraphQL::Types::ISO8601DateTime, null: false + field :is_ready, Boolean, null: false, method: :ready? end end diff --git a/twenty-backend/share/twenty-backend/schema.graphql b/twenty-backend/share/twenty-backend/schema.graphql index b139b9c..47b7d4a 100644 --- a/twenty-backend/share/twenty-backend/schema.graphql +++ b/twenty-backend/share/twenty-backend/schema.graphql @@ -44,12 +44,13 @@ type Project { type Query { findTask(taskId: Int!): Task projects: [Project!]! - tasks: [Task!]! + tasks(status: TaskStatus!): [Task!]! } type Task { content: String! id: Int! + isReady: Boolean! project: Project! status: TaskStatus! title: String! @@ -57,9 +58,10 @@ type Task { } input TaskInput { - content: String! - projectId: Int! - title: String! + content: String + projectId: Int + status: TaskStatus + title: String } enum TaskStatus { diff --git a/twenty-frontend/Gemfile b/twenty-frontend/Gemfile index 174680c..359ed7b 100644 --- a/twenty-frontend/Gemfile +++ b/twenty-frontend/Gemfile @@ -3,6 +3,6 @@ source "https://rubygems.org" gemspec gem "nanoc", "~> 4.12" gem "nanoc-live", "~> 1.0" -gem "nanoc-webpack.rb", "~> 0.4" +gem "nanoc-webpack.rb", "~> 0.5" gem "sass", "~> 3.7" gem "rainpress", "~> 1.0" diff --git a/twenty-frontend/Gemfile.lock b/twenty-frontend/Gemfile.lock index 28f525b..4d14c1a 100644 --- a/twenty-frontend/Gemfile.lock +++ b/twenty-frontend/Gemfile.lock @@ -75,7 +75,7 @@ GEM listen (~> 3.0) nanoc-cli (~> 4.11, >= 4.11.14) nanoc-core (~> 4.11, >= 4.11.14) - nanoc-webpack.rb (0.4.5) + nanoc-webpack.rb (0.5.5) ryo.rb (~> 0.4) parallel (1.24.0) pastel (0.8.0) @@ -123,7 +123,7 @@ PLATFORMS DEPENDENCIES nanoc (~> 4.12) nanoc-live (~> 1.0) - nanoc-webpack.rb (~> 0.4) + nanoc-webpack.rb (~> 0.5) rainpress (~> 1.0) rake (~> 13.0) sass (~> 3.7) diff --git a/twenty-frontend/src/css/_global.scss b/twenty-frontend/src/css/_global.scss index 0704d86..7a13894 100644 --- a/twenty-frontend/src/css/_global.scss +++ b/twenty-frontend/src/css/_global.scss @@ -8,6 +8,9 @@ body .wrapper { color: $accent-color; text-decoration: none; } + &:hover { + text-decoration: underline; + } } ul { margin: 0; diff --git a/twenty-frontend/src/css/_groups.scss b/twenty-frontend/src/css/_groups.scss new file mode 100644 index 0000000..78b834d --- /dev/null +++ b/twenty-frontend/src/css/_groups.scss @@ -0,0 +1,80 @@ +body .wrapper .group { + @import "colors"; + width: 100%; + margin-bottom: 10px; + .group-name { + margin: 10px 0 10px 0; + padding: 10px; + background: $secondary-color; + color: $primary-color; + font-size: small; + border-radius: 5px; + } + + .group-items { + min-height: 75px; + + p { + padding-top: 2.5%; + font-size: small; + } + + ul.items { + li.item { + display: flex; + padding: 7.5px; + border: 1px solid $primary-color; + + select { + height: 35px; + padding: 10px; + background: $primary-color; + font-size: smaller; + } + + &:hover { + background: $secondary-color; + color: $primary-color; + border-radius: 10px; + border: 1px solid $secondary-color; + + a { + color: $primary-color; + .subtitle { + color: $primary-color; + } + } + } + + a { + display: flex; + flex-direction: column; + height: 50px; + justify-content: space-between; + padding: 10px 10px 5px 0px; + + .title { + display: flex; + padding-bottom: 5px; + font-size: smaller; + } + + .subtitle { + font-size: small; + color: $secondary-color; + } + } + + .tags { + .tag { + color: $primary-color; + border: 1px solid $secondary-color; + border-radius: 5px; + font-size: small; + font-weight: bold; + } + } + } + } + } +} diff --git a/twenty-frontend/src/css/_layout.scss b/twenty-frontend/src/css/_layout.scss index 328595a..03d4d84 100644 --- a/twenty-frontend/src/css/_layout.scss +++ b/twenty-frontend/src/css/_layout.scss @@ -6,6 +6,7 @@ width: 20%; } .column-2 { + h1, h2, h3 { font-size: medium; } width: 100%; } } @@ -22,6 +23,7 @@ .w-100 { width: 100%; } .w-95 { width: 95%; } .w-85 { width: 85%; } +.w-75 { width: 75%; } .h-100 { height: 100%; } .h-80 { height: 80%; } .h-50 { height: 50%; } diff --git a/twenty-frontend/src/css/_lists.scss b/twenty-frontend/src/css/_lists.scss index 8214185..c474e7a 100644 --- a/twenty-frontend/src/css/_lists.scss +++ b/twenty-frontend/src/css/_lists.scss @@ -4,8 +4,8 @@ ----------------- **/ -ul.collection { - h1 { +ul { + h1, h2 { display: flex; align-items: center; margin: 0; @@ -16,83 +16,15 @@ ul.collection { width: 100%; font-size: medium; } + h2 { font-size: small; } } -ul.collection li.item { - display: flex; - flex-wrap: wrap; - height: 85px; - - padding: 0px 2.5px 0 2.5px; - border-left: 1px solid $primary-color; - border-right: 1px solid $primary-color; - - &:hover { - border-left: 1px solid $secondary-color; - border-right: 1px solid $secondary-color; - } - - a { - display: flex; - flex-direction: column; - height: 50px; - justify-content: space-between; - padding: 10px 10px 5px 0px; - .title { - display: flex; - padding-bottom: 5px; - font-size: smaller; - } - - .subtitle { - font-size: small; - color: $secondary-color; - } - } - .tags { - .tag { - color: $primary-color; - border: 1px solid $secondary-color; - border-radius: 5px; - font-size: small; - font-weight: bold; - } - } - - .break { - display: flex; - width: 100%; - } - - ul.actions { - list-style-type: none; - width: 5%; - display: flex; - place-content: flex-end; - li { - display: flex; - align-items: flex-start; - } - } -} - -ul.collection li.item.removed { - animation: bounceOutDown; - animation-duration: 0.5s; -} - -ul.collection li.item.completed { - animation: bounceOutUp; - animation-duration: 0.5s; -} ul.items { @import "colors"; margin: 0; padding: 0; list-style-type: none; - - } ul.items.nav { @@ -147,3 +79,15 @@ ul.items.tasks { } } } + +ul.action-group { + display: flex; + list-style-type: none; + width: 5%; + place-content: flex-end; + + li { + display: flex; + align-items: flex-start; + } +} diff --git a/twenty-frontend/src/css/_panels.scss b/twenty-frontend/src/css/_panels.scss deleted file mode 100644 index af14d70..0000000 --- a/twenty-frontend/src/css/_panels.scss +++ /dev/null @@ -1,76 +0,0 @@ -.panel h1 { - @import "colors"; - display: flex; - align-items: center; - margin: 0; - padding: 0; - background: $primary-color; - color: $secondary-color; - height: 25px; - width: 100%; - font-size: medium; -} - -.panel .panel-header.panel-tabs { - @import "colors"; - padding: 5px 5px 0 0px; - ul.tabs { - list-style-type: none; - display: flex; - height: 100%; - margin: 0; - padding: 0; - li:first-child { - border-left: none; - } - li { - font-size: small; - height: 100%; - margin-right: 5px; - border: 1px solid $accent-color; - border-bottom: none; - padding: 10px 5px 5px 10px; - border-radius: 5px; - min-width: 120px; - text-align: center; - background: $primary-color; - color: $secondary-color; - cursor: pointer; - opacity: 0.5; - } - li.active, li:hover { - font-weight: bold; - color: $secondary-color; - opacity: 1; - } - } -} - -.panel .panel-body { - @import "colors"; - width: 100%; - .task.content { - padding: 10px; - ul { - padding: revert; - } - ul li { - line-height: 1.7em; - } - h3,h4,h5 { - &:first-child { - margin: 0 0 5px 0; - } - margin: 15px 0 5px 0; - } - code { - padding: 0px 5px 0px 5px; - font-family: "Noto Sans Mono Regular"; - font-size: smaller; - font-weight: bold; - color: lighten(#FF0000, 25%); - border-radius: 5px; - border: 1px solid $accent-color; - } - } -} diff --git a/twenty-frontend/src/css/main.scss b/twenty-frontend/src/css/main.scss index 5b48dd9..b674617 100644 --- a/twenty-frontend/src/css/main.scss +++ b/twenty-frontend/src/css/main.scss @@ -2,7 +2,7 @@ @import "colors"; @import "global"; @import "layout"; -@import "panels"; +@import "groups"; @import "lists"; @import "vendor/forms"; @@ -34,12 +34,12 @@ body { padding-top: 25px; } - .trash.icon, .done.icon { + .destroy-task.icon, .complete-task.icon, .start-task.icon { height: 20px; width: 20px; cursor: pointer; } - .trash.icon { + .destroy-task.icon { g { fill: #d9534f; } diff --git a/twenty-frontend/src/js/components/Group.tsx b/twenty-frontend/src/js/components/Group.tsx new file mode 100644 index 0000000..a11cc2b --- /dev/null +++ b/twenty-frontend/src/js/components/Group.tsx @@ -0,0 +1,72 @@ +import React from "react"; +import type { Task } from "/types/schema"; +import classnames from "classnames"; +import { DateTime } from "luxon"; +import { QueryResult } from "@apollo/client"; +import { TaskStatusSelect } from "/components/TaskStatusSelect"; + +type Props = { + groupName: string; + getItems: () => QueryResult; +}; + +export function Group({ groupName, getItems }: Props) { + const { data, loading } = getItems(); + const items = data?.tasks; + + if (loading) { + return null; + } + + return ( +
+

{groupName}

+
+ {items.length ? ( + + ) : ( +

+ There are no {groupName.toLowerCase()} tasks. +
+ + Add a task + + . +

+ )} +
+
+ ); +} diff --git a/twenty-frontend/src/js/components/Icons.tsx b/twenty-frontend/src/js/components/Icons.tsx index 3f96b2f..160b39a 100644 --- a/twenty-frontend/src/js/components/Icons.tsx +++ b/twenty-frontend/src/js/components/Icons.tsx @@ -1,43 +1,32 @@ import React from "react"; -type IconProps = { - onClick: (e: React.MouseEvent) => void; - title?: string; -}; +type Props = { name: IconName; onClick: (e: React.MouseEvent) => void }; +type IconName = "start-task" | "complete-task" | "destroy-task"; +type IconSet = Record JSX.Element>; -export function TrashIcon({ onClick, title }: IconProps) { - return ( +const icons: IconSet = { + "start-task": ({ name, onClick }: Props) => ( onClick(e)} - className="trash icon" - aria-labelledby="trash-icon-title" + viewBox="0 0 64 64" + onClick={onClick} + className={`${name} icon`} > - {title} - - - - - + + + + + - ); -} - -export function DoneIcon({ onClick, title }: IconProps) { - return ( + ), + "complete-task": ({ name, onClick }: Props) => ( onClick(e)} - className="done icon" - aria-labelledby="complete-icon-title" + viewBox="0 0 24 24" + onClick={onClick} + className={`${name} icon`} > - {title} - ); + ), + "destroy-task": ({ name, onClick }: Props) => ( + + + + + + + + + ), +}; + +export function Icon({ name, onClick }: Props) { + const getIcon = icons[name]; + return getIcon({ name, onClick }); } diff --git a/twenty-frontend/src/js/components/Task.tsx b/twenty-frontend/src/js/components/Task.tsx index 05b9c4b..a11dca0 100644 --- a/twenty-frontend/src/js/components/Task.tsx +++ b/twenty-frontend/src/js/components/Task.tsx @@ -71,77 +71,62 @@ export function Task({ taskId }: { taskId?: number }) {
-
-
+ +

{task ? "Edit task" : "New task"}

-
-
    -
  • setIsEditable(true)} - > - Write -
  • -
  • setIsEditable(false)} - > - Preview -
  • -
+
+
+
+
-
-
- -
-
- -
- {isEditable ? ( - <> -
-