Fix: Mobile responsiveness audit (modals, views, sidebar) & Critical bug fixes (registration, orphan tasks)
continuous-integration/drone/push Build is passing
continuous-integration/drone/push Build is passing
This commit is contained in:
@@ -78,7 +78,7 @@ export function ChangePasswordModal({ open, onOpenChange }: ChangePasswordModalP
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||||
<DialogContent>
|
<DialogContent className="w-[95vw] max-w-lg sm:max-w-md p-4 sm:p-6">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>{t('auth.changePassword')}</DialogTitle>
|
<DialogTitle>{t('auth.changePassword')}</DialogTitle>
|
||||||
<DialogDescription>
|
<DialogDescription>
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ export function ShareAccessModal({ open, onOpenChange }: ShareAccessModalProps)
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
<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>
|
<DialogHeader>
|
||||||
<DialogTitle className="flex items-center gap-2">
|
<DialogTitle className="flex items-center gap-2">
|
||||||
<Users className="w-5 h-5" />
|
<Users className="w-5 h-5" />
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ export default function TaskCreationModal({ isOpen, onClose, onSave }: TaskCreat
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={isOpen} onOpenChange={handleClose} >
|
<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>
|
<DialogHeader>
|
||||||
<DialogTitle className="flex items-center gap-2">
|
<DialogTitle className="flex items-center gap-2">
|
||||||
<Plus className="w-4 h-4" />
|
<Plus className="w-4 h-4" />
|
||||||
@@ -182,7 +182,7 @@ export default function TaskCreationModal({ isOpen, onClose, onSave }: TaskCreat
|
|||||||
/>
|
/>
|
||||||
</div>
|
</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)}>
|
<Select value={priority} onValueChange={(value: 'low' | 'medium' | 'high') => setPriority(value)}>
|
||||||
<SelectTrigger data-testid="select-task-priority">
|
<SelectTrigger data-testid="select-task-priority">
|
||||||
<SelectValue placeholder={t('taskCreation.priority')} />
|
<SelectValue placeholder={t('taskCreation.priority')} />
|
||||||
|
|||||||
@@ -323,7 +323,7 @@ export default function TaskDetailsModal({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={isOpen} onOpenChange={handleClose}>
|
<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>
|
<DialogHeader>
|
||||||
<DialogTitle className="flex items-center gap-2">
|
<DialogTitle className="flex items-center gap-2">
|
||||||
<FileText className="w-5 h-5" />
|
<FileText className="w-5 h-5" />
|
||||||
|
|||||||
@@ -274,7 +274,7 @@ export default function TasksWithCalendar({ tasks, onTaskUpdate, onTaskEdit, onT
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex gap-3">
|
<div className="flex flex-wrap gap-2 sm:gap-3">
|
||||||
<Select value={filterBy} onValueChange={(value: FilterOption) => {
|
<Select value={filterBy} onValueChange={(value: FilterOption) => {
|
||||||
setFilterBy(value);
|
setFilterBy(value);
|
||||||
console.log('Filter changed to:', value);
|
console.log('Filter changed to:', value);
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ export default function TimeCompletionModal({ isOpen, onClose, onSave, taskTitle
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={isOpen} onOpenChange={handleClose}>
|
<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>
|
<DialogHeader>
|
||||||
<DialogTitle className="flex items-center gap-2">
|
<DialogTitle className="flex items-center gap-2">
|
||||||
<Timer className="w-4 h-4" />
|
<Timer className="w-4 h-4" />
|
||||||
|
|||||||
@@ -142,7 +142,7 @@ const ScheduleSettings = ({ user }: { user: User }) => {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</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">
|
<div className="space-y-2">
|
||||||
<label className="text-sm font-medium">{t('settings.schedule.start')}</label>
|
<label className="text-sm font-medium">{t('settings.schedule.start')}</label>
|
||||||
<Input type="time" value={currentSchedule.start} onChange={(e) => handleChange('start', e.target.value)} />
|
<Input type="time" value={currentSchedule.start} onChange={(e) => handleChange('start', e.target.value)} />
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "rest-express",
|
"name": "rest-express",
|
||||||
"version": "1.0.9",
|
"version": "1.0.10",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "rest-express",
|
"name": "rest-express",
|
||||||
"version": "1.0.9",
|
"version": "1.0.10",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@dnd-kit/core": "^6.3.1",
|
"@dnd-kit/core": "^6.3.1",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "rest-express",
|
"name": "rest-express",
|
||||||
"version": "1.0.9",
|
"version": "1.0.10",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
+1
-2
@@ -105,8 +105,7 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
app.get("/api/settings/public", async (req, res) => {
|
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"
|
// Default to true if not set, or specifically check for "false"
|
||||||
res.json({ registration_enabled: regEnabled !== "false" });
|
res.json({ registration_enabled: regEnabled !== "false" });
|
||||||
});
|
});
|
||||||
|
|||||||
+12
-3
@@ -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 { 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 * as schema from "@shared/schema";
|
||||||
import { getDatabase, pool } from "./db";
|
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 { randomUUID } from "crypto";
|
||||||
import session from "express-session";
|
import session from "express-session";
|
||||||
import createMemoryStore from "memorystore";
|
import createMemoryStore from "memorystore";
|
||||||
@@ -847,8 +847,17 @@ export class DbStorage implements IStorage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getTasksForUser(userId: string): Promise<Task[]> {
|
async getTasksForUser(userId: string): Promise<Task[]> {
|
||||||
// 1. My tasks
|
// 1. My tasks + Orphans (invalid/null userId)
|
||||||
const result = await this.db.select().from(schema.tasks).where(eq(schema.tasks.userId, 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
|
// 2. Shared Tasks
|
||||||
const sharedLinks = await this.db.select().from(schema.sharedTasks).where(eq(schema.sharedTasks.sharedWithUserId, userId));
|
const sharedLinks = await this.db.select().from(schema.sharedTasks).where(eq(schema.sharedTasks.sharedWithUserId, userId));
|
||||||
|
|||||||
Reference in New Issue
Block a user