Fix: Mobile responsiveness audit (modals, views, sidebar) & Critical bug fixes (registration, orphan tasks)
continuous-integration/drone/push Build is passing

This commit is contained in:
2025-12-17 15:06:13 +01:00
parent 2579df0b89
commit 877d0c897e
11 changed files with 34 additions and 26 deletions
@@ -78,7 +78,7 @@ export function ChangePasswordModal({ open, onOpenChange }: ChangePasswordModalP
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent>
<DialogContent className="w-[95vw] max-w-lg sm:max-w-md p-4 sm:p-6">
<DialogHeader>
<DialogTitle>{t('auth.changePassword')}</DialogTitle>
<DialogDescription>
+1 -1
View File
@@ -49,7 +49,7 @@ export function ShareAccessModal({ open, onOpenChange }: ShareAccessModalProps)
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="sm:max-w-md">
<DialogContent className="w-[95vw] max-w-lg sm:max-w-md p-4 sm:p-6">
<DialogHeader>
<DialogTitle className="flex items-center gap-2">
<Users className="w-5 h-5" />
+2 -2
View File
@@ -129,7 +129,7 @@ export default function TaskCreationModal({ isOpen, onClose, onSave }: TaskCreat
return (
<Dialog open={isOpen} onOpenChange={handleClose} >
<DialogContent className="sm:max-w-md mx-4 max-h-[90vh] overflow-y-auto">
<DialogContent className="w-[95vw] max-w-lg sm:max-w-md max-h-[90vh] overflow-y-auto p-4 sm:p-6">
<DialogHeader>
<DialogTitle className="flex items-center gap-2">
<Plus className="w-4 h-4" />
@@ -182,7 +182,7 @@ export default function TaskCreationModal({ isOpen, onClose, onSave }: TaskCreat
/>
</div>
<div className="grid grid-cols-2 gap-3">
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
<Select value={priority} onValueChange={(value: 'low' | 'medium' | 'high') => setPriority(value)}>
<SelectTrigger data-testid="select-task-priority">
<SelectValue placeholder={t('taskCreation.priority')} />
+1 -1
View File
@@ -323,7 +323,7 @@ export default function TaskDetailsModal({
return (
<Dialog open={isOpen} onOpenChange={handleClose}>
<DialogContent className="sm:max-w-2xl max-h-[90vh] overflow-y-auto">
<DialogContent className="w-[95vw] max-w-lg sm:max-w-2xl max-h-[90vh] overflow-y-auto p-4 sm:p-6">
<DialogHeader>
<DialogTitle className="flex items-center gap-2">
<FileText className="w-5 h-5" />
+1 -1
View File
@@ -274,7 +274,7 @@ export default function TasksWithCalendar({ tasks, onTaskUpdate, onTaskEdit, onT
/>
</div>
<div className="flex gap-3">
<div className="flex flex-wrap gap-2 sm:gap-3">
<Select value={filterBy} onValueChange={(value: FilterOption) => {
setFilterBy(value);
console.log('Filter changed to:', value);
+11 -11
View File
@@ -20,14 +20,14 @@ export default function TimeCompletionModal({ isOpen, onClose, onSave, taskTitle
const handleSave = () => {
let totalMinutes = 0;
if (activeTab === 'clock') {
totalMinutes = hours * 60 + minutes;
} else {
const parsedHours = parseFloat(manualHours) || 0;
totalMinutes = Math.round(parsedHours * 60);
}
onSave(totalMinutes);
console.log(`Time logged: ${totalMinutes} minutes for task: ${taskTitle}`);
handleClose();
@@ -58,14 +58,14 @@ export default function TimeCompletionModal({ isOpen, onClose, onSave, taskTitle
return (
<Dialog open={isOpen} onOpenChange={handleClose}>
<DialogContent className="sm:max-w-md mx-4">
<DialogContent className="w-[95vw] max-w-lg sm:max-w-md p-4 sm:p-6">
<DialogHeader>
<DialogTitle className="flex items-center gap-2">
<Timer className="w-4 h-4" />
Task Completed!
</DialogTitle>
</DialogHeader>
<div className="space-y-4">
<div className="text-center">
<p className="text-sm text-muted-foreground mb-2">
@@ -96,7 +96,7 @@ export default function TimeCompletionModal({ isOpen, onClose, onSave, taskTitle
{formatTime(hours, minutes)}
</div>
</div>
<div className="grid grid-cols-2 gap-4">
{/* Hours Picker */}
<div className="space-y-2">
@@ -116,7 +116,7 @@ export default function TimeCompletionModal({ isOpen, onClose, onSave, taskTitle
))}
</div>
</div>
{/* Minutes Picker */}
<div className="space-y-2">
<label className="text-sm font-medium text-center block">Minutes</label>
@@ -147,7 +147,7 @@ export default function TimeCompletionModal({ isOpen, onClose, onSave, taskTitle
{getCurrentTimeDisplay()}
</div>
</div>
<div>
<label className="text-sm font-medium block mb-2">Hours (decimal)</label>
<Input
@@ -167,17 +167,17 @@ export default function TimeCompletionModal({ isOpen, onClose, onSave, taskTitle
</div>
</TabsContent>
</Tabs>
<div className="flex gap-3 pt-2">
<Button
variant="outline"
<Button
variant="outline"
onClick={handleClose}
className="flex-1"
data-testid="button-cancel-time"
>
Cancel
</Button>
<Button
<Button
onClick={handleSave}
disabled={activeTab === 'clock' ? hours === 0 && minutes === 0 : !manualHours.trim()}
className="flex-1"
+1 -1
View File
@@ -142,7 +142,7 @@ const ScheduleSettings = ({ user }: { user: User }) => {
</button>
</div>
<div className="grid grid-cols-2 gap-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="space-y-2">
<label className="text-sm font-medium">{t('settings.schedule.start')}</label>
<Input type="time" value={currentSchedule.start} onChange={(e) => handleChange('start', e.target.value)} />
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "rest-express",
"version": "1.0.9",
"version": "1.0.10",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "rest-express",
"version": "1.0.9",
"version": "1.0.10",
"license": "MIT",
"dependencies": {
"@dnd-kit/core": "^6.3.1",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "rest-express",
"version": "1.0.9",
"version": "1.0.10",
"type": "module",
"license": "MIT",
"scripts": {
+1 -2
View File
@@ -105,8 +105,7 @@ export async function registerRoutes(app: Express): Promise<Server> {
});
app.get("/api/settings/public", async (req, res) => {
const regEnabled = "true"; // Force enabled for testing
// const regEnabled = await storage.getSystemSettings("registration_enabled");
const regEnabled = await storage.getSystemSettings("registration_enabled");
// Default to true if not set, or specifically check for "false"
res.json({ registration_enabled: regEnabled !== "false" });
});
+12 -3
View File
@@ -1,7 +1,7 @@
import { type User, type InsertUser, type Label, type InsertLabel, type SharedLabel, type Task, type InsertTask, type XpEvent, type InsertXpEvent, type Goal, type InsertGoal, type Reward, type InsertReward, type UserReward, type InsertUserReward, type SystemSettings, type InsertSystemSettings, type SharedTask, type InsertSharedTask, type UserTaskAccess, type InsertUserTaskAccess, type InsertPasswordResetToken, type PasswordResetToken, type Conversation, type InsertConversation, type Message, type InsertMessage, type AuditLog, type InsertAuditLog } from "@shared/schema";
import * as schema from "@shared/schema";
import { getDatabase, pool } from "./db";
import { eq, sql, and, desc, asc, gt, ne } from "drizzle-orm";
import { eq, sql, and, desc, asc, gt, ne, or, isNull } from "drizzle-orm";
import { randomUUID } from "crypto";
import session from "express-session";
import createMemoryStore from "memorystore";
@@ -847,8 +847,17 @@ export class DbStorage implements IStorage {
}
async getTasksForUser(userId: string): Promise<Task[]> {
// 1. My tasks
const result = await this.db.select().from(schema.tasks).where(eq(schema.tasks.userId, userId));
// 1. My tasks + Orphans (invalid/null userId)
// We use leftJoin to users to identify tasks with non-existent userIds (orphans)
const resultRaw = await this.db.select({ task: schema.tasks })
.from(schema.tasks)
.leftJoin(schema.users, eq(schema.tasks.userId, schema.users.id))
.where(or(
eq(schema.tasks.userId, userId), // Owned by me
isNull(schema.tasks.userId), // No owner
isNull(schema.users.id) // Owner ID exists but User doesn't (deleted user)
));
const result = resultRaw.map(r => r.task);
// 2. Shared Tasks
const sharedLinks = await this.db.select().from(schema.sharedTasks).where(eq(schema.sharedTasks.sharedWithUserId, userId));