fix: 4 bugs — MSK today events, settings scroll, note dates, persistent notes volume
All checks were successful
Deploy / deploy (push) Successful in 4m35s
All checks were successful
Deploy / deploy (push) Successful in 4m35s
- calendar API: today/week ranges use Moscow time (UTC+3) instead of UTC — previously today events did not appear until 03:00 MSK - settings tab: add -webkit-overflow-scrolling: touch + touchAction pan-y for tablet scroll - NotesTab: add date picker (pinDate) in editor header + date badge in list - home: pinnedNotes now filters by pinDate (today or future), falls back to latest - notes/auth: storage moved from /tmp to /data (falls back to /tmp if /data missing) - deploy workflow: mount /opt/digital-home/smart-home-tablet-data:/data so notes survive redeploys
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
import { useState, useEffect, useCallback } from 'react'
|
||||
import { Plus, X, Trash2, ShoppingCart, FileText, Check, Circle } from 'lucide-react'
|
||||
import { Plus, X, Trash2, ShoppingCart, FileText, Check, Circle, Calendar as CalendarIcon } from 'lucide-react'
|
||||
|
||||
interface NoteItem {
|
||||
id: string
|
||||
@@ -15,6 +15,7 @@ interface Note {
|
||||
items?: NoteItem[]
|
||||
text?: string
|
||||
color: string
|
||||
pinDate: string | null
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
@@ -157,6 +158,12 @@ export default function NotesTab() {
|
||||
{note.title}
|
||||
</span>
|
||||
</div>
|
||||
{note.pinDate && (
|
||||
<div style={{ fontSize: 11, color: note.color, fontWeight: 600, marginBottom: 4, display: 'flex', alignItems: 'center', gap: 4 }}>
|
||||
<CalendarIcon size={11} />
|
||||
{new Date(note.pinDate).toLocaleDateString('ru-RU', { day: 'numeric', month: 'short' })}
|
||||
</div>
|
||||
)}
|
||||
{note.type === 'shopping' && totalCount > 0 && (
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
||||
<div style={{
|
||||
@@ -208,30 +215,67 @@ export default function NotesTab() {
|
||||
{/* Header */}
|
||||
<div style={{
|
||||
padding: '18px 24px', borderBottom: '1px solid rgba(255,255,255,0.05)',
|
||||
display: 'flex', alignItems: 'center', justifyContent: 'space-between',
|
||||
display: 'flex', flexDirection: 'column', gap: 10,
|
||||
background: `${activeNote.color}08`,
|
||||
}}>
|
||||
<input
|
||||
value={activeNote.title}
|
||||
onChange={e => {
|
||||
const newTitle = e.target.value
|
||||
setActiveNote(prev => prev ? { ...prev, title: newTitle } : null)
|
||||
}}
|
||||
onBlur={() => updateNote(activeNote.id, { title: activeNote.title })}
|
||||
style={{
|
||||
background: 'transparent', border: 'none', outline: 'none',
|
||||
fontSize: 18, fontWeight: 700, color: 'var(--text-primary)',
|
||||
fontFamily: 'inherit', flex: 1, minWidth: 0,
|
||||
}}
|
||||
/>
|
||||
<button onClick={() => { if (confirm('Удалить заметку?')) deleteNote(activeNote.id) }} style={{
|
||||
width: 32, height: 32, borderRadius: 10,
|
||||
background: 'rgba(239,68,68,0.08)',
|
||||
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||
color: '#f87171',
|
||||
}}>
|
||||
<Trash2 size={15} />
|
||||
</button>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12 }}>
|
||||
<input
|
||||
value={activeNote.title}
|
||||
onChange={e => {
|
||||
const newTitle = e.target.value
|
||||
setActiveNote(prev => prev ? { ...prev, title: newTitle } : null)
|
||||
}}
|
||||
onBlur={() => updateNote(activeNote.id, { title: activeNote.title })}
|
||||
style={{
|
||||
background: 'transparent', border: 'none', outline: 'none',
|
||||
fontSize: 18, fontWeight: 700, color: 'var(--text-primary)',
|
||||
fontFamily: 'inherit', flex: 1, minWidth: 0,
|
||||
}}
|
||||
/>
|
||||
<button onClick={() => { if (confirm('Удалить заметку?')) deleteNote(activeNote.id) }} style={{
|
||||
width: 32, height: 32, borderRadius: 10,
|
||||
background: 'rgba(239,68,68,0.08)',
|
||||
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||
color: '#f87171', flexShrink: 0,
|
||||
}}>
|
||||
<Trash2 size={15} />
|
||||
</button>
|
||||
</div>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
|
||||
<CalendarIcon size={14} color={activeNote.color} />
|
||||
<input
|
||||
type="date"
|
||||
value={activeNote.pinDate || ''}
|
||||
onChange={e => {
|
||||
const v = e.target.value || null
|
||||
setActiveNote(prev => prev ? { ...prev, pinDate: v } : null)
|
||||
updateNote(activeNote.id, { pinDate: v })
|
||||
}}
|
||||
style={{
|
||||
background: 'rgba(255,255,255,0.04)',
|
||||
border: '1px solid rgba(255,255,255,0.07)',
|
||||
borderRadius: 10, padding: '6px 10px',
|
||||
color: 'var(--text-primary)', fontSize: 13,
|
||||
outline: 'none', fontFamily: 'inherit',
|
||||
colorScheme: 'dark' as any,
|
||||
}}
|
||||
/>
|
||||
{activeNote.pinDate && (
|
||||
<button
|
||||
onClick={() => {
|
||||
setActiveNote(prev => prev ? { ...prev, pinDate: null } : null)
|
||||
updateNote(activeNote.id, { pinDate: null })
|
||||
}}
|
||||
style={{
|
||||
background: 'transparent', border: 'none',
|
||||
color: 'var(--text-tertiary)', fontSize: 12,
|
||||
padding: '4px 8px', cursor: 'pointer',
|
||||
}}
|
||||
>
|
||||
Очистить
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
|
||||
Reference in New Issue
Block a user