Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 120 additions & 40 deletions src/app/saved/saved-mobile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import Image from 'next/image';
import axios from 'axios';
import { PopupViewTT } from '@/components/ui/PopupMobile';
import Loader from '@/components/ui/Loader';
import Popup from '@/components/ui/Popup';
import { ZButton } from '@/components/ui/Buttons';

async function fetchTimetablesByOwner(owner: string) {
const res = await axios.get(`/api/timetables?owner=${encodeURIComponent(owner)}`);
Expand Down Expand Up @@ -45,6 +47,9 @@ export default function SavedMobile() {
const [popupTitle, setPopupTitle] = useState<string>('');
const [selectedTT, setSelectedTT] = useState<TimetableEntry | null>(null);
const [publicToggle, setPublicToggle] = useState(true);

// NEW: for rename support
const [renameValue, setRenameValue] = useState('');

useEffect(() => {
if (!userEmail) return;
Expand Down Expand Up @@ -72,11 +77,29 @@ export default function SavedMobile() {
setShowPopup(true);
}

// NEW: delete handler (mirrors saved.tsx)
async function handleDelete() {
if (!selectedTT) return;
await axios.delete(`/api/timetables/${selectedTT._id}`);
setTimetables(prev => prev.filter(t => t._id !== selectedTT._id));
setShowPopup(false);
setSelectedTT(null);
}

// NEW: rename handler (mirrors saved.tsx)
async function handleRename() {
if (!selectedTT) return;
await axios.patch(`/api/timetables/${selectedTT._id}`, { title: renameValue });
setTimetables(prev =>
prev.map(t => (t._id === selectedTT._id ? { ...t, title: renameValue } : t))
);
setShowPopup(false);
setSelectedTT(null);
}

async function handleCopyLink(tt: TimetableEntry) {
try {
await axios.patch(`/api/timetables/${tt._id}`, {
isPublic: publicToggle,
});
await axios.patch(`/api/timetables/${tt._id}`, { isPublic: publicToggle });
const res = await axios.get(`/api/timetables/${tt._id}`);
const updated = res.data;
if (!updated.shareId) throw new Error('No shareId found');
Expand All @@ -88,16 +111,15 @@ export default function SavedMobile() {
async function handleTogglePublic(state: 'on' | 'off') {
if (!selectedTT) return;
setPublicToggle(state === 'on');
await axios.patch(`/api/timetables/${selectedTT._id}`, {
isPublic: state === 'on',
});
await axios.patch(`/api/timetables/${selectedTT._id}`, { isPublic: state === 'on' });
setTimetables(prev =>
prev.map(tt => (tt._id === selectedTT._id ? { ...tt, isPublic: state === 'on' } : tt))
);
}

return (
<div className="flex flex-col min-h-screen relative items-center font-poppins">
{/* Background */}
<div className="absolute inset-0 -z-10 bg-[#CEE4E5]">
<Image
src="/art/bg_dots.svg"
Expand All @@ -113,43 +135,84 @@ export default function SavedMobile() {

<Navbar page="mobile" />

<div className="text-4xl mb-8 mt-28 text-black font-pangolin">Saved Timetables</div>

<ul className="w-full space-y-4 px-6">
{timetables.map((tt, index) => (
<li
key={tt._id}
className="grid grid-cols-[auto_1fr] items-center px-3 py-3 bg-[#A7D5D7] text-black rounded-xl border-2 border-black shadow-[2px_2px_0_0_black]"
onClick={() => handleView(tt)}
>
<span className="font-medium text-base mr-4">{index + 1}.</span>
<span className="font-medium text-base truncate">{tt.title}</span>
</li>
))}
</ul>

<div className="flex items-center w-full mt-8 mb-8 text-sm font-poppins font-semibold text-black/50 px-8">
<div className="flex-grow h-0.5 bg-gradient-to-r from-transparent to-black/33" />
{loading ? null : timetables.length === 0 ? (
<span className="mx-4">Nothing To Show Here</span>
) : (
<span className="mx-4">End of List</span>
)}
<div className="flex-grow h-0.5 bg-gradient-to-r from-black/33 to-transparent" />
</div>
{/* FIX: reduced mt-28 → mt-20 so title isn't pushed too far down on short phones */}
<div className="text-4xl mb-8 mt-20 text-black font-pangolin">Saved Timetables</div>

{/* FIX: unified loading/empty/list rendering — no orphaned divider during load */}
{loading ? (
<Loader />
<div className="flex-1 flex items-center justify-center">
<Loader />
</div>
) : timetables.length === 0 ? (
<div className="mx-auto my-auto text-center text-sm font-poppins font-semibold text-black/70">
No saved timetables found.
<br />
Create and save from the desktop website.
<div className="flex-1 flex items-center justify-center px-8">
<p className="text-center text-sm font-semibold text-black/70">
No saved timetables found.
<br />
Create and save from the desktop website.
</p>
</div>
) : null}
) : (
<>
<ul className="w-full space-y-4 px-6">
{timetables.map((tt, index) => (
<li
key={tt._id}
className="grid grid-cols-[auto_1fr_auto] items-center px-3 py-3 bg-[#A7D5D7] text-black rounded-xl border-2 border-black shadow-[2px_2px_0_0_black] active:opacity-70 transition-opacity"
>
{/* Tapping the number or title opens the view popup */}
<span
className="font-medium text-base mr-4"
onClick={() => handleView(tt)}
>
{index + 1}.
</span>
<span
className="font-medium text-base truncate"
onClick={() => handleView(tt)}
>
{tt.title}
</span>

{/* NEW: action buttons for rename and delete on mobile */}
<div className="flex gap-2 ml-2" onClick={e => e.stopPropagation()}>
<ZButton
type="image"
color="blue"
image="/icons/edit.svg"
onClick={() => {
setSelectedTT(tt);
setRenameValue(tt.title);
setPopupType('rename_tt');
setShowPopup(true);
}}
/>
<ZButton
type="image"
color="red"
image="/icons/trash.svg"
onClick={() => {
setSelectedTT(tt);
setPopupType('delete_tt');
setShowPopup(true);
}}
/>
</div>
</li>
))}
</ul>

{/* Divider only shown when list is visible */}
<div className="flex items-center w-full mt-8 mb-8 text-sm font-poppins font-semibold text-black/50 px-8">
<div className="flex-grow h-0.5 bg-gradient-to-r from-transparent to-black/33" />
<span className="mx-4">End of List</span>
<div className="flex-grow h-0.5 bg-gradient-to-r from-black/33 to-transparent" />
</div>
</>
)}

<Footer type="mobile" />

{/* View popup — unchanged */}
{showPopup && popupType === 'view_tt' && selectedTT && (
<PopupViewTT
TTName={popupTitle}
Expand All @@ -160,13 +223,30 @@ export default function SavedMobile() {
shareSwitchAction={handleTogglePublic}
shareLink={
selectedTT.shareId
? `${
typeof window !== 'undefined' ? window.location.origin : ''
}/share/${selectedTT.shareId}`
? `${typeof window !== 'undefined' ? window.location.origin : ''}/share/${selectedTT.shareId}`
: ''
}
/>
)}
</div>

{showPopup && popupType === 'delete_tt' && selectedTT && (
<Popup
type="delete_tt"
dataBody={selectedTT.title}
closeLink={() => setShowPopup(false)}
action={handleDelete}
/>
)}

{showPopup && popupType === 'rename_tt' && selectedTT && (
<Popup
type="rename_tt"
dataBody={renameValue}
closeLink={() => setShowPopup(false)}
action={handleRename}
onInputChange={setRenameValue}
/>
)}
</div>
);
}
7 changes: 1 addition & 6 deletions src/app/slots/slots.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -180,12 +180,7 @@ export default function View() {
<div className="mx-auto mt-12 mb-10 w-full max-w-[1000px]">
<div className="overflow-x-auto">
<div className="min-w-[1000px] h-[480px]">
<TimeTable
slotNames={active.map(i => ({
slotName: buttonTexts[i],
showName: true,
}))}
/>
<TimeTable ... />
</div>
</div>
</div>
Expand Down