fix: synchronize calendar month input and add month navigation
This commit is contained in:
@@ -1004,3 +1004,9 @@ Taslak türü seçicisine erişilebilir ad eklendi. Üretim derlemesi geçti. Ö
|
|||||||
- Gün seçiminde aria-pressed ve tam tarihli erişilebilir ad eklendi. İhale kartları klavye davranışı elle yazılmış div yerine doğal button oldu. Mevcut ay seçicisi korunur.
|
- Gün seçiminde aria-pressed ve tam tarihli erişilebilir ad eklendi. İhale kartları klavye davranışı elle yazılmış div yerine doğal button oldu. Mevcut ay seçicisi korunur.
|
||||||
- 227 otomatik test ve derleme geçti. Karışık saat sırası, bilinmeyen saat, geçersiz/eksik tarihin kaybolmaması, geçmiş kayıt ve girdi sırasının değiştirilmemesi test edildi.
|
- 227 otomatik test ve derleme geçti. Karışık saat sırası, bilinmeyen saat, geçersiz/eksik tarihin kaybolmaması, geçmiş kayıt ve girdi sırasının değiştirilmemesi test edildi.
|
||||||
- Yeni takvim kartlarının tarayıcı/mobil kabulü ve Google/Outlook aktarım kabulü açık. Bu değişiklik ICS aktarımını test etmiş sayılmaz. Canlıya yayın yapılmadı.
|
- Yeni takvim kartlarının tarayıcı/mobil kabulü ve Google/Outlook aktarım kabulü açık. Bu değişiklik ICS aktarımını test etmiş sayılmaz. Canlıya yayın yapılmadı.
|
||||||
|
|
||||||
|
### Takvim ay girişinin düzeltilmesi ve tarayıcı kabulü — 6 Eylül 2026
|
||||||
|
- Tarayıcıda month alanı Ekim değerini gösterirken takvim günleri Eylül'de kaldı; alandan çıkmak da güncellemedi. Ay seçimi onInput ile de işlenerek görünüm/state uyuşmazlığı giderildi. Önceki ay, sonraki ay ve bugüne dön düğmeleri eklendi; yıl geçişleri ve desteklenen yıl sınırları doğrulanır.
|
||||||
|
- İzole QA tarayıcısında altı kontrol geçti: 22 Eylül'deki 10:30 Akdeniz önce, 11:00 Kayseri sonra; Ekim girişinde 31 gün ve 1 Ekim başlığı; sonraki ay Kasım; önceki ay Ekim; bugüne dönüş; Akdeniz detayında aynı tarih/saat. Kanıt qa-calendar-navigation.json.
|
||||||
|
- 228 otomatik test ve derleme geçti. Test sunucusunun çıktısında önceki firma alanı değişikliğinde yinelenmiş aria-invalid niteliği görüldü; tek niteliğe indirildi. Son derleme bu uyarı olmadan geçti; büyük paket uyarısı sürüyor.
|
||||||
|
- QA sekmesi ve yalnız bu test için açılan sunucu kapatıldı (exit 0). Mobil genişlikler, tarihi bilinmeyen kaydın tarayıcı senaryosu ve Google/Outlook aktarımı bu kabul kapsamında değil; açık kalır. Canlıya yayın yapılmadı.
|
||||||
|
|||||||
@@ -340,7 +340,6 @@ export const BidPackageBuilder = ({ results, tender: selectedTender, initialFocu
|
|||||||
</span>
|
</span>
|
||||||
<input
|
<input
|
||||||
ref={targetRef(`firm:${a.k}`)}
|
ref={targetRef(`firm:${a.k}`)}
|
||||||
aria-invalid={eksik || undefined}
|
|
||||||
type={a.k==='eposta' ? 'email' : a.k==='telefon' ? 'tel' : 'text'}
|
type={a.k==='eposta' ? 'email' : a.k==='telefon' ? 'tel' : 'text'}
|
||||||
aria-invalid={eksik}
|
aria-invalid={eksik}
|
||||||
aria-describedby={eksik ? `${fieldPrefix}-firm-error-${a.k}` : undefined}
|
aria-describedby={eksik ? `${fieldPrefix}-firm-error-${a.k}` : undefined}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useDeadlineClock } from '../../hooks/useDeadlineClock';
|
import { useDeadlineClock } from '../../hooks/useDeadlineClock';
|
||||||
import { calendarMonthDays, istanbulDateKey, calendarAgenda } from '../../services/tenderCalendar';
|
import { calendarMonthDays, istanbulDateKey, calendarAgenda, shiftCalendarMonth } from '../../services/tenderCalendar';
|
||||||
import React, { useState, useMemo } from 'react';
|
import React, { useState, useMemo } from 'react';
|
||||||
import { useApp } from '../../context/AppContext';
|
import { useApp } from '../../context/AppContext';
|
||||||
import { CalendarDays } from 'lucide-react';
|
import { CalendarDays } from 'lucide-react';
|
||||||
@@ -13,6 +13,7 @@ export const TenderCalendar = () => {
|
|||||||
const agenda=useMemo(()=>calendarAgenda(tenders,now),[tenders,now]);
|
const agenda=useMemo(()=>calendarAgenda(tenders,now),[tenders,now]);
|
||||||
const daysList=calendarMonthDays(month).map(day=>({...day,count:agenda.days.get(day.key)?.length || 0}));
|
const daysList=calendarMonthDays(month).map(day=>({...day,count:agenda.days.get(day.key)?.length || 0}));
|
||||||
const dayTenders=agenda.days.get(selectedDay) || [];
|
const dayTenders=agenda.days.get(selectedDay) || [];
|
||||||
|
const chooseMonth=value=>{if(calendarMonthDays(value).length){setMonth(value);setSelectedDay(value+'-01');}};
|
||||||
const card=({tender:t,info})=><button key={t.id} type="button" onClick={()=>setActiveTenderDetail(t)}
|
const card=({tender:t,info})=><button key={t.id} type="button" onClick={()=>setActiveTenderDetail(t)}
|
||||||
className="w-full text-left bg-white rounded-xl border border-slate-200 p-4 shadow-xs hover:shadow-md transition-all flex flex-wrap items-center justify-between gap-3">
|
className="w-full text-left bg-white rounded-xl border border-slate-200 p-4 shadow-xs hover:shadow-md transition-all flex flex-wrap items-center justify-between gap-3">
|
||||||
<span className="min-w-0 space-y-1">
|
<span className="min-w-0 space-y-1">
|
||||||
@@ -44,9 +45,14 @@ export const TenderCalendar = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<label className="block text-sm font-semibold">Takvim ayı
|
<label className="block text-sm font-semibold">Takvim ayı
|
||||||
<input type="month" value={month} onChange={e => {if(calendarMonthDays(e.target.value).length){setMonth(e.target.value);setSelectedDay(e.target.value+'-01');}}}
|
<input type="month" value={month} onInput={e=>chooseMonth(e.currentTarget.value)} onChange={e=>chooseMonth(e.target.value)}
|
||||||
className="ml-3 border border-slate-300 rounded-lg p-2"/>
|
className="ml-3 border border-slate-300 rounded-lg p-2"/>
|
||||||
</label>
|
</label>
|
||||||
|
<div className="flex flex-wrap gap-2" aria-label="Takvim ayı gezinme">
|
||||||
|
<button type="button" className="border rounded px-3 py-2 text-sm" disabled={!shiftCalendarMonth(month,-1)} onClick={()=>chooseMonth(shiftCalendarMonth(month,-1))}>Önceki ay</button>
|
||||||
|
<button type="button" className="border rounded px-3 py-2 text-sm" onClick={()=>{setMonth(today.slice(0,7));setSelectedDay(today);}}>Bugüne dön</button>
|
||||||
|
<button type="button" className="border rounded px-3 py-2 text-sm" disabled={!shiftCalendarMonth(month,1)} onClick={()=>chooseMonth(shiftCalendarMonth(month,1))}>Sonraki ay</button>
|
||||||
|
</div>
|
||||||
{/* Day Selector Buttons */}
|
{/* Day Selector Buttons */}
|
||||||
<div className="grid grid-cols-2 sm:grid-cols-4 lg:grid-cols-7 gap-2">
|
<div className="grid grid-cols-2 sm:grid-cols-4 lg:grid-cols-7 gap-2">
|
||||||
{daysList.map(d => (
|
{daysList.map(d => (
|
||||||
|
|||||||
@@ -86,3 +86,10 @@ export function calendarAgenda(tenders, now=new Date()) {
|
|||||||
undated.sort(compare);
|
undated.sort(compare);
|
||||||
return {days,undated};
|
return {days,undated};
|
||||||
}
|
}
|
||||||
|
export function shiftCalendarMonth(month,delta) {
|
||||||
|
if(!/^\d{4}-(0[1-9]|1[0-2])$/.test(month) || !Number.isInteger(delta))return null;
|
||||||
|
const [year,m]=month.split('-').map(Number),index=year*12+m-1+delta;
|
||||||
|
const nextYear=Math.floor(index/12),nextMonth=index%12+1;
|
||||||
|
if(nextYear<1 || nextYear>9999)return null;
|
||||||
|
return `${String(nextYear).padStart(4,'0')}-${String(nextMonth).padStart(2,'0')}`;
|
||||||
|
}
|
||||||
|
|||||||
@@ -79,3 +79,12 @@ test('calendar agenda sorts known times before unknown and preserves undated rec
|
|||||||
assert.equal([...agenda.days.values()].flat().length+agenda.undated.length,records.length);
|
assert.equal([...agenda.days.values()].flat().length+agenda.undated.length,records.length);
|
||||||
assert.equal(records[0].id,'late');
|
assert.equal(records[0].id,'late');
|
||||||
});
|
});
|
||||||
|
test('calendar month navigation crosses year boundaries without invalid months',async()=>{
|
||||||
|
const {shiftCalendarMonth}=await import('../src/services/tenderCalendar.js');
|
||||||
|
assert.equal(shiftCalendarMonth('2026-12',1),'2027-01');
|
||||||
|
assert.equal(shiftCalendarMonth('2026-01',-1),'2025-12');
|
||||||
|
assert.equal(shiftCalendarMonth('2024-02',1),'2024-03');
|
||||||
|
assert.equal(shiftCalendarMonth('9999-12',1),null);
|
||||||
|
assert.equal(shiftCalendarMonth('0001-01',-1),null);
|
||||||
|
assert.equal(shiftCalendarMonth('2026-13',1),null);
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user