fix mobile screen responsive
This commit is contained in:
@@ -10,6 +10,16 @@ const EXPANDED_WIDTH = 620;
|
||||
const GUTTER = 24;
|
||||
/** Expanded must never dominate: the dashboard stays the primary experience. */
|
||||
const MAX_VIEWPORT_SHARE = 0.42;
|
||||
/**
|
||||
* Below this width there is no room for a column *beside* the dashboard — a
|
||||
* 380px track on a 375px phone collapses `main` to nothing — so the panel
|
||||
* becomes a bottom sheet over the page instead of an inline column.
|
||||
*
|
||||
* Deliberately Tailwind's `md` (768px), not `lg`: tablets already lay the
|
||||
* inline column out acceptably, so they keep the existing behaviour and only
|
||||
* phones switch to the sheet.
|
||||
*/
|
||||
const OVERLAY_BREAKPOINT = 768;
|
||||
|
||||
/** Tracks viewport width so the expanded panel can be clamped on smaller screens. */
|
||||
function useViewportWidth() {
|
||||
@@ -58,6 +68,45 @@ export function AssistantPanel({ stickyClassName, panelHeightClassName }) {
|
||||
const trackWidth = panelWidth + GUTTER;
|
||||
const easing = [0.65, 0, 0.35, 1];
|
||||
|
||||
// Phone/small-tablet: a sheet over the dashboard rather than a column beside
|
||||
// it. Same assistant, same props — only the container differs.
|
||||
if (viewportWidth < OVERLAY_BREAKPOINT) {
|
||||
return (
|
||||
<AnimatePresence initial={false}>
|
||||
{isOpen && context && (
|
||||
<>
|
||||
<motion.div
|
||||
className="fixed inset-0 z-40 bg-black/40"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
transition={{ duration: 0.25, ease: easing }}
|
||||
onClick={close}
|
||||
/>
|
||||
<motion.aside
|
||||
key="assistant-sheet"
|
||||
aria-label="Krow AI Assistant"
|
||||
className="fixed inset-x-0 bottom-0 z-50 max-h-[85dvh] overflow-y-auto overscroll-contain rounded-t-2xl bg-white shadow-2xl"
|
||||
initial={{ y: '100%' }}
|
||||
animate={{ y: 0 }}
|
||||
exit={{ y: '100%' }}
|
||||
transition={{ duration: 0.3, ease: easing }}
|
||||
>
|
||||
<KrowAssistant
|
||||
key={context.id}
|
||||
context={context}
|
||||
expanded={false}
|
||||
onClose={close}
|
||||
onExpand={expand}
|
||||
onRestore={restore}
|
||||
/>
|
||||
</motion.aside>
|
||||
</>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<AnimatePresence initial={false}>
|
||||
{isOpen && context && (
|
||||
|
||||
@@ -123,7 +123,7 @@ export default function Layout() {
|
||||
<div className="min-h-screen gradient-mesh bg-[#F8FAFC]">
|
||||
{/* Header */}
|
||||
<header className="sticky top-0 z-40 glass border-b border-white/40">
|
||||
<div className="mx-auto max-w-container-wide px-6 h-16 flex items-center justify-between gap-4">
|
||||
<div className="mx-auto max-w-container-wide px-4 sm:px-6 h-16 flex items-center justify-between gap-4">
|
||||
{/* Brand */}
|
||||
<button onClick={() => navigate(ROLE_HOME[activeRole])} className="flex items-center gap-2 text-left shrink-0 group">
|
||||
<Image src={KROW_LOGO_URL} alt="KROW" fittingType="fit" className="h-7 w-[86px] rounded-lg shadow-lg shadow-blue-500/20 group-hover:scale-105 transition-transform" />
|
||||
@@ -243,8 +243,13 @@ export default function Layout() {
|
||||
{/* Control bar (Employer / Admin only) */}
|
||||
{!isTalent && (
|
||||
<div className="sticky top-16 z-30 glass border-b border-white/40">
|
||||
<div className="mx-auto max-w-container-wide px-6 h-14 flex items-center gap-3">
|
||||
<form onSubmit={handleSearch} className="relative flex-1 max-w-md">
|
||||
{/* Below `sm` this wraps to two rows: search gets a full-width row of
|
||||
its own, the filter + action share the next. On one row the search
|
||||
field was being crushed to ~90px by the status select's intrinsic
|
||||
width, clipping its own placeholder. From `sm` up it is the
|
||||
original single 56px-tall row. */}
|
||||
<div className="mx-auto max-w-container-wide px-4 sm:px-6 py-2.5 sm:py-0 sm:h-14 flex flex-wrap sm:flex-nowrap items-center gap-2 sm:gap-3">
|
||||
<form onSubmit={handleSearch} className="relative w-full sm:w-auto sm:flex-1 sm:max-w-md">
|
||||
<Search className="absolute left-3.5 top-1/2 -translate-y-1/2 w-4 h-4 text-[#9CA3AF]" />
|
||||
<input
|
||||
value={searchQuery}
|
||||
@@ -255,12 +260,12 @@ export default function Layout() {
|
||||
/>
|
||||
</form>
|
||||
|
||||
<div className="flex items-center gap-2 ml-auto">
|
||||
<div className="relative">
|
||||
<div className="flex items-center gap-2 w-full sm:w-auto ml-auto">
|
||||
<div className="relative flex-1 sm:flex-none min-w-0">
|
||||
<select
|
||||
value={statusFilter}
|
||||
onChange={(e) => handleStatusChange(e.target.value)}
|
||||
className="appearance-none h-9 pl-3 pr-8 text-[13px] font-medium rounded-full border border-white/60 bg-white/70 backdrop-blur-sm focus:border-[#0A39DF] focus:outline-none cursor-pointer"
|
||||
className="appearance-none w-full sm:w-auto h-9 pl-3 pr-8 text-[13px] font-medium rounded-full border border-white/60 bg-white/70 backdrop-blur-sm focus:border-[#0A39DF] focus:outline-none cursor-pointer"
|
||||
>
|
||||
{STATUS_OPTIONS.map((s) => (
|
||||
<option key={s} value={s}>{s}</option>
|
||||
@@ -273,7 +278,8 @@ export default function Layout() {
|
||||
className="inline-flex items-center gap-1.5 bg-[#0A39DF] hover:bg-[#082EB4] text-white text-[13px] font-semibold px-4 py-2 rounded-full transition-all shrink-0 hover:scale-[1.02] shadow-sm"
|
||||
>
|
||||
<Plus className="w-4 h-4" />
|
||||
<span className="hidden sm:inline">New Position</span>
|
||||
{/* The second row leaves room for the label on mobile too */}
|
||||
<span>New Position</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -372,7 +378,7 @@ export default function Layout() {
|
||||
layout is identical to the original. Opening the assistant animates a
|
||||
track open beside it; `main` is a flex child, so it reflows smoothly
|
||||
rather than jumping, and closing restores the original layout exactly. */}
|
||||
<div className="mx-auto flex max-w-container-wide px-6 py-8">
|
||||
<div className="mx-auto flex max-w-container-wide px-4 sm:px-6 py-6 sm:py-8">
|
||||
<main className="min-w-0 w-full max-w-7xl flex-1 mx-auto">
|
||||
<Outlet />
|
||||
</main>
|
||||
|
||||
@@ -78,11 +78,11 @@ export default function Overview() {
|
||||
transition={{ duration: 0.5 }}
|
||||
className="flex items-center justify-between flex-wrap gap-4"
|
||||
>
|
||||
<div>
|
||||
<h1 className="text-3xl font-heading font-bold text-[#111827]">
|
||||
<div className="min-w-0">
|
||||
<h1 className="text-2xl sm:text-3xl font-heading font-bold text-[#111827]">
|
||||
Good day. <span className="gradient-text">Let's hire smarter.</span>
|
||||
</h1>
|
||||
<p className="text-[14px] text-[#6B7280] mt-1">AI-powered hiring · No quizzes · Real readiness</p>
|
||||
<p className="text-[13px] sm:text-[14px] text-[#6B7280] mt-1">AI-powered hiring · No quizzes · Real readiness</p>
|
||||
</div>
|
||||
<AssistantTrigger />
|
||||
</motion.div>
|
||||
|
||||
Reference in New Issue
Block a user