Skip to content

Commit a6b9feb

Browse files
committed
Add mobile status action sheet
1 parent 3b92aaf commit a6b9feb

16 files changed

Lines changed: 149 additions & 1 deletion

File tree

app/_components/FeatureComponents/Kanban/KanbanCard.tsx

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { CSS } from "@dnd-kit/utilities";
55
import { Item, Checklist, KanbanStatus } from "@/app/_types";
66
import { cn } from "@/app/_utils/global-utils";
77
import { Dropdown } from "@/app/_components/GlobalComponents/Dropdowns/Dropdown";
8+
import { Modal } from "@/app/_components/GlobalComponents/Modals/Modal";
89
import { useState, useEffect, memo, useMemo, useCallback } from "react";
910
import { TaskStatus } from "@/app/_types/enums";
1011
import { KanbanCardDetail } from "./KanbanCardDetail";
@@ -72,6 +73,7 @@ const KanbanCardComponent = ({
7273

7374
const [showDetailModal, setShowDetailModal] = useState(false);
7475
const [showTimeEntriesModal, setShowTimeEntriesModal] = useState(false);
76+
const [showStatusSheet, setShowStatusSheet] = useState(false);
7577
const hideMobileStatusDropdown = user?.hideMobileStatusDropdown === "enable";
7678

7779
const kanbanItemHook = useKanbanItem({
@@ -116,8 +118,47 @@ const KanbanCardComponent = ({
116118
return options?.sort((a, b) => a.order - b.order);
117119
}, [statuses]);
118120

121+
const handleMobileStatusChange = async (newStatus: string) => {
122+
await kanbanItemHook.handleStatusChange(newStatus);
123+
setShowStatusSheet(false);
124+
};
125+
119126
return (
120127
<>
128+
{showStatusSheet && (
129+
<Modal
130+
isOpen={showStatusSheet}
131+
onClose={() => setShowStatusSheet(false)}
132+
title={t("kanban.changeStatus")}
133+
className="lg:hidden"
134+
>
135+
<div className="space-y-2">
136+
{statusOptions.map((status) => {
137+
const isCurrent = status.id === (item.status || TaskStatus.TODO);
138+
return (
139+
<button
140+
key={status.id}
141+
type="button"
142+
onClick={() => handleMobileStatusChange(status.id.toString())}
143+
className={cn(
144+
"w-full flex items-center gap-3 px-3 py-2.5 rounded-jotty border text-left transition-colors",
145+
isCurrent
146+
? "border-primary/50 bg-primary/5 text-foreground font-semibold"
147+
: "border-border text-muted-foreground hover:border-primary/30 hover:bg-muted/50"
148+
)}
149+
>
150+
<span
151+
className="h-3 w-3 rounded-full flex-shrink-0"
152+
style={{ backgroundColor: status.color || "#6b7280" }}
153+
/>
154+
<span className="text-sm">{status.name}</span>
155+
</button>
156+
);
157+
})}
158+
</div>
159+
</Modal>
160+
)}
161+
121162
{showTimeEntriesModal && item.timeEntries && (
122163
<TimeEntriesModal
123164
isOpen={showTimeEntriesModal}
@@ -172,6 +213,7 @@ const KanbanCardComponent = ({
172213
onEditSave={kanbanItemHook.handleSave}
173214
onEditKeyDown={kanbanItemHook.handleKeyDown}
174215
onShowSubtaskModal={() => setShowDetailModal(true)}
216+
onShowStatusMenu={() => setShowStatusSheet(true)}
175217
onEdit={kanbanItemHook.handleEdit}
176218
onDelete={kanbanItemHook.handleDelete}
177219
onArchive={kanbanItemHook.handleArchive}

app/_components/FeatureComponents/Kanban/KanbanItemContent.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ interface KanbanItemContentProps {
2323
onEditSave: () => void;
2424
onEditKeyDown: (e: React.KeyboardEvent) => void;
2525
onShowSubtaskModal: () => void;
26+
onShowStatusMenu: () => void;
2627
onEdit: () => void;
2728
onDelete: () => void;
2829
onArchive: () => void;
@@ -43,6 +44,7 @@ const KanbanItemContentComponent = ({
4344
onEditSave,
4445
onEditKeyDown,
4546
onShowSubtaskModal,
47+
onShowStatusMenu,
4648
onEdit,
4749
onDelete,
4850
onArchive,
@@ -110,6 +112,15 @@ const KanbanItemContentComponent = ({
110112
value=""
111113
options={[
112114
{ id: "view", name: t("tasks.viewTask") },
115+
...(permissions?.canEdit
116+
? [
117+
{
118+
id: "status",
119+
name: t("kanban.changeStatus"),
120+
className: "lg:hidden",
121+
},
122+
]
123+
: []),
113124
...(permissions?.canEdit
114125
? [{ id: "add", name: t("tasks.addSubtask") }]
115126
: []),
@@ -128,6 +139,9 @@ const KanbanItemContentComponent = ({
128139
case "view":
129140
onShowSubtaskModal();
130141
break;
142+
case "status":
143+
onShowStatusMenu();
144+
break;
131145
case "add":
132146
onShowSubtaskModal();
133147
break;

app/_components/GlobalComponents/Dropdowns/Dropdown.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ interface DropdownOption {
1010
name: React.ReactNode;
1111
icon?: React.ComponentType<{ className?: string }>;
1212
colors?: { background: string; primary: string } | null;
13+
className?: string;
1314
}
1415

1516
interface DropdownProps {
@@ -135,7 +136,8 @@ export const Dropdown = ({
135136
onClick={(e) => handleSelect(e, option.id.toString())}
136137
className={cn(
137138
"w-full flex items-center gap-2 px-3 py-2 text-md lg:text-sm hover:bg-accent hover:text-accent-foreground",
138-
option.id === value && "bg-accent text-accent-foreground"
139+
option.id === value && "bg-accent text-accent-foreground",
140+
option.className
139141
)}
140142
>
141143
{option.icon && <option.icon className="h-4 w-4" />}

app/_translations/de.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,7 @@
574574
"itemTitle": "Elementtitel",
575575
"manageStatuses": "Status verwalten",
576576
"viewArchived": "Archivierte anzeigen",
577+
"changeStatus": "Status ändern",
577578
"noBoards": "Keine Boards gefunden",
578579
"noBoardsYet": "Noch keine Kanban-Boards",
579580
"createFirstBoard": "Erstelle dein erstes Kanban-Board, um deine Projekte zu verwalten.",

app/_translations/en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -599,6 +599,7 @@
599599
"itemTitle": "Item Title",
600600
"manageStatuses": "Manage Statuses",
601601
"viewArchived": "View Archived",
602+
"changeStatus": "Change status",
602603
"noBoards": "No boards found",
603604
"noBoardsYet": "No kanban boards yet",
604605
"createFirstBoard": "Create your first kanban board to start managing your projects.",

app/_translations/es.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,8 @@
543543
"autoComplete": "Autocompletar"
544544
},
545545
"kanban": {
546+
"properties": "Propiedades",
547+
"status": "Estado",
546548
"priority": "Prioridad",
547549
"score": "Puntuación",
548550
"assignee": "Asignado a",
@@ -572,6 +574,7 @@
572574
"itemTitle": "Título del elemento",
573575
"manageStatuses": "Gestionar estados",
574576
"viewArchived": "Ver archivados",
577+
"changeStatus": "Cambiar estado",
575578
"noBoards": "No se encontraron tableros",
576579
"noBoardsYet": "Aún no hay tableros Kanban",
577580
"createFirstBoard": "Cree su primer tablero Kanban para empezar a gestionar sus proyectos.",
@@ -836,6 +839,11 @@
836839
"showStatusOnCards": "Mostrar estado",
837840
"hideStatusOnCards": "Ocultar estado",
838841
"hideStatusOnCardsDescription": "Oculta la etiqueta de estado en las tarjetas Kanban. El estado ya se indica por la columna en la que se encuentra la tarjeta.",
842+
"hideMobileStatusDropdownLabel": "Menú desplegable de estado móvil en tarjetas",
843+
"showMobileStatusDropdown": "Mostrar menú móvil",
844+
"hideMobileStatusDropdown": "Ocultar menú móvil",
845+
"selectMobileStatusDropdown": "Seleccionar comportamiento del menú de estado móvil",
846+
"hideMobileStatusDropdownDescription": "Controla el menú desplegable de estado siempre visible en tarjetas Kanban móviles. La configuración de la etiqueta de estado es independiente.",
839847
"selectStatusOnCards": "Seleccionar visibilidad del estado",
840848
"codeBlockChoice": "Elija el estilo del bloque de código",
841849
"themedCodeBlock": "Bloque de código temático",

app/_translations/fr.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,8 @@
543543
"autoComplete": "Complétion automatique"
544544
},
545545
"kanban": {
546+
"properties": "Propriétés",
547+
"status": "Statut",
546548
"priority": "Priorité",
547549
"score": "Score",
548550
"assignee": "Assigné à",
@@ -572,6 +574,7 @@
572574
"itemTitle": "Titre de l'élément",
573575
"manageStatuses": "Gérer les statuts",
574576
"viewArchived": "Voir les archivés",
577+
"changeStatus": "Changer le statut",
575578
"noBoards": "Aucun tableau trouvé",
576579
"noBoardsYet": "Pas encore de tableaux Kanban",
577580
"createFirstBoard": "Créez votre premier tableau Kanban pour gérer vos projets.",
@@ -836,6 +839,11 @@
836839
"showStatusOnCards": "Afficher le statut",
837840
"hideStatusOnCards": "Masquer le statut",
838841
"hideStatusOnCardsDescription": "Masque l'étiquette de statut sur les cartes Kanban. Le statut est déjà indiqué par la colonne dans laquelle se trouve la carte.",
842+
"hideMobileStatusDropdownLabel": "Menu déroulant de statut mobile sur les cartes",
843+
"showMobileStatusDropdown": "Afficher le menu mobile",
844+
"hideMobileStatusDropdown": "Masquer le menu mobile",
845+
"selectMobileStatusDropdown": "Sélectionner le comportement du menu de statut mobile",
846+
"hideMobileStatusDropdownDescription": "Contrôle le menu déroulant de statut toujours visible sur les cartes Kanban mobiles. Le réglage de l'étiquette de statut est séparé.",
839847
"selectStatusOnCards": "Sélectionner la visibilité du statut",
840848
"codeBlockChoice": "Choisissez le style de bloc de code",
841849
"themedCodeBlock": "Bloc de code thématique",

app/_translations/it.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,8 @@
543543
"autoComplete": "Complete automaticamente"
544544
},
545545
"kanban": {
546+
"properties": "Proprietà",
547+
"status": "Stato",
546548
"priority": "Priorità",
547549
"score": "Punteggio",
548550
"assignee": "Assegnato a",
@@ -572,6 +574,7 @@
572574
"itemTitle": "Titolo elemento",
573575
"manageStatuses": "Gestisci stati",
574576
"viewArchived": "Visualizza archiviati",
577+
"changeStatus": "Cambia stato",
575578
"noBoards": "Nessuna board trovata",
576579
"noBoardsYet": "Nessuna board Kanban ancora",
577580
"createFirstBoard": "Crea la tua prima board Kanban per gestire i tuoi progetti.",
@@ -836,6 +839,11 @@
836839
"showStatusOnCards": "Mostra stato",
837840
"hideStatusOnCards": "Nascondi stato",
838841
"hideStatusOnCardsDescription": "Nasconde l'etichetta di stato dalle schede Kanban. Lo stato è già indicato dalla colonna in cui si trova la scheda.",
842+
"hideMobileStatusDropdownLabel": "Menu a discesa stato mobile sulle schede",
843+
"showMobileStatusDropdown": "Mostra menu mobile",
844+
"hideMobileStatusDropdown": "Nascondi menu mobile",
845+
"selectMobileStatusDropdown": "Seleziona comportamento del menu stato mobile",
846+
"hideMobileStatusDropdownDescription": "Controlla il menu a discesa dello stato sempre visibile sulle schede Kanban mobili. L'impostazione dell'etichetta di stato è separata.",
839847
"selectStatusOnCards": "Seleziona visibilità stato",
840848
"codeBlockChoice": "Scegli lo stile del blocco di codice",
841849
"themedCodeBlock": "Blocco di codice a tema",

app/_translations/klingon.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,8 @@
568568
"autoComplete": "nIteb rIn"
569569
},
570570
"kanban": {
571+
"properties": "DI'onmey",
572+
"status": "ghu'",
571573
"priority": "nom mIw",
572574
"score": "pong",
573575
"assignee": "ghu' ghoS",
@@ -597,6 +599,7 @@
597599
"itemTitle": "chel pong",
598600
"manageStatuses": "ghu'mey yIvu'",
599601
"viewArchived": "Pol yIlegh",
602+
"changeStatus": "ghu' yIchoH",
600603
"noBoards": "beQmey lutu'lu'be'",
601604
"noBoardsYet": "Kanban beQmey not yet",
602605
"createFirstBoard": "Kanban beQ wa' yIchel 'ej QInmey yIvu'.",
@@ -861,6 +864,11 @@
861864
"showStatusOnCards": "ngoq legh",
862865
"hideStatusOnCards": "ngoq So'",
863866
"hideStatusOnCardsDescription": "Kanban ghItlhmey ngoq So'. ngoq DaH ram vIghItlh Daq legh.",
867+
"hideMobileStatusDropdownLabel": "mobile ghu' menu ghItlhmeyDaq",
868+
"showMobileStatusDropdown": "mobile menu yIcha'",
869+
"hideMobileStatusDropdown": "mobile menu yISo'",
870+
"selectMobileStatusDropdown": "mobile ghu' menu mIw yIwIv",
871+
"hideMobileStatusDropdownDescription": "mobile Kanban ghItlhmeyDaq reH leghlu'bogh ghu' menu SeH. ngoq label setting latlh.",
864872
"selectStatusOnCards": "ngoq leghlaHghach wIv",
865873
"codeBlockChoice": "Choose the code block style",
866874
"themedCodeBlock": "Themed codeblock",

app/_translations/ko.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,8 @@
568568
"autoComplete": "자동 완료"
569569
},
570570
"kanban": {
571+
"properties": "속성",
572+
"status": "상태",
571573
"priority": "우선순위",
572574
"score": "점수",
573575
"assignee": "담당자",
@@ -597,6 +599,7 @@
597599
"itemTitle": "항목 제목",
598600
"manageStatuses": "상태 관리",
599601
"viewArchived": "보관된 항목 보기",
602+
"changeStatus": "상태 변경",
600603
"noBoards": "보드를 찾을 수 없습니다",
601604
"noBoardsYet": "아직 칸반 보드가 없습니다",
602605
"createFirstBoard": "프로젝트 관리를 위해 첫 번째 칸반 보드를 만드세요.",
@@ -861,6 +864,11 @@
861864
"showStatusOnCards": "상태 표시",
862865
"hideStatusOnCards": "상태 숨기기",
863866
"hideStatusOnCardsDescription": "칸반 카드에서 상태 레이블을 숨깁니다. 상태는 카드가 있는 열로 이미 표시됩니다.",
867+
"hideMobileStatusDropdownLabel": "카드의 모바일 상태 드롭다운",
868+
"showMobileStatusDropdown": "모바일 드롭다운 표시",
869+
"hideMobileStatusDropdown": "모바일 드롭다운 숨기기",
870+
"selectMobileStatusDropdown": "모바일 상태 드롭다운 동작 선택",
871+
"hideMobileStatusDropdownDescription": "모바일 칸반 카드에서 항상 보이는 상태 드롭다운을 제어합니다. 상태 레이블 설정은 별도입니다.",
864872
"selectStatusOnCards": "상태 표시 여부 선택",
865873
"codeBlockChoice": "코드 블록 스타일을 선택하세요",
866874
"themedCodeBlock": "테마 코드블록",

0 commit comments

Comments
 (0)