Repository: hulib-engineering/hulib
Pull request #265: fix: card and availableCard UI
Author: login: TuCongDanh12
Labels: name: released
Created: 2025-05-21T17:33:21Z | Merged: 2025-05-26T11:45:26Z



Diff:
diff --git a/src/components/common/HeadSlots/index.tsx b/src/components/common/HeadSlots/index.tsx
index 3108eac7..451b65a9 100644
--- a/src/components/common/HeadSlots/index.tsx
+++ b/src/components/common/HeadSlots/index.tsx
@@ -7,6 +7,7 @@ interface Props {
   dayOfWeek: any;
   onChangeDayOfWeek: Function;
   selectedTimes: any;
+  availableSlot?: boolean;
 }
 
 const days = {
@@ -51,12 +52,14 @@ export default function HeadSlots({
   dayOfWeek = 0,
   onChangeDayOfWeek,
   selectedTimes,
+  availableSlot = false,
 }: Props) {
   const [selectedDay, setSelectedDay] = useState<keyof typeof days>(dayOfWeek);
   const [numberOfTimeSlots, setNumberOfTimeSlots] = useState<number>(0);
 
   useEffect(() => {
     setSelectedDay(dayOfWeek);
+    console.log('dayOfWeek', dayOfWeek);
   }, [dayOfWeek]);
 
   useEffect(() => {
@@ -90,15 +93,23 @@ export default function HeadSlots({
 
       {/* Message below the day selector */}
       <Typography
-        className="mt-3 text-center"
+        className={`mt-3 ${
+          availableSlot ? 'text-left text-xs' : 'text-center'
+        }`}
         sx={{
-          color: numberOfTimeSlots > 0 ? '#38AA16' : '#e83e8c',
-          fontSize: '1.1rem',
+          color: availableSlot
+            ? '#38AA16'
+            : numberOfTimeSlots > 0
+              ? '#38AA16'
+              : '#e83e8c',
+          fontSize: availableSlot ? '0.75rem' : '1.1rem',
         }}
       >
         {numberOfTimeSlots > 0
           ? `Amazing!!! You can meet ${numberOfTimeSlots} Libers every ${days[selectedDay].name}! 💚`
-          : days[selectedDay].text}
+          : availableSlot
+            ? 'You did not choose any available time slot in this day, update now to meet with more people'
+            : days[selectedDay].text}
       </Typography>
     </Box>
   );
diff --git a/src/components/schedule/components/BigSchedule/BigSchedule.tsx b/src/components/schedule/components/BigSchedule/BigSchedule.tsx
index 902b8b0b..e076b6b3 100644
--- a/src/components/schedule/components/BigSchedule/BigSchedule.tsx
+++ b/src/components/schedule/components/BigSchedule/BigSchedule.tsx
@@ -95,20 +95,22 @@ export default function BigCalendar() {
 
     return (
       <div
-        className="group relative z-[50] cursor-pointer overflow-visible"
+        className="group relative z-[50] h-[68px] cursor-pointer overflow-visible"
         onMouseEnter={(e) => handleMouseEnter(e, extendedProps)}
         onMouseLeave={handleMouseLeave}
       >
-        <div className="relative min-w-[60px] overflow-visible">
+        <div className="relative h-full min-w-[60px] overflow-visible">
           <div
-            className={`relative flex flex-col justify-start overflow-visible rounded-md border border-[#fff] ${
-              isHumanBook ? 'bg-[#CDDDFE]' : 'bg-[#FFE3CC]'
+            className={`relative flex h-full flex-col  justify-center overflow-visible rounded-xl border  ${
+              isHumanBook
+                ? 'border-[#FEF3C7] bg-[#FFFBEB]'
+                : 'border-[#DBEAFE] bg-[#DBEAFE]'
             } p-[2px]`}
           >
             {isPending && (
-              <p className="absolute left-[-20px] top-[-20px] flex h-[24px] w-[82px] items-center justify-center rounded-[100px] border-l-[#fff] bg-[#FFC745] p-[7px] text-[14px] font-[500] leading-[16px] text-[#000]">
-                Pending...
-              </p>
+              <span className=" inline-block h-[24px] w-[80px] self-end rounded-[100px] border-l-[#fff] bg-[#FFEDD5] p-[7px] text-right text-[12px] font-[500] leading-[16px] text-[#F97316]">
+                Waiting...
+              </span>
             )}
             <div className="flex items-center">
               <Image
@@ -119,14 +121,18 @@ export default function BigCalendar() {
                 loading="lazy"
                 className="mr-[2px] rounded-full border border-[#fff]"
               />
-              <p className="h-[20px] w-[80px] truncate text-[#171819]">
+              <p className="h-[20px] w-[80px] truncate text-xs text-[#171819]">
                 {isHumanBook
-                  ? extendedProps.humanBook.fullName
-                  : extendedProps.reader?.fullName}
+                  ? extendedProps.reader.fullName
+                  : extendedProps.humanBook?.fullName}
               </p>
             </div>
-            <p className={isHumanBook ? 'text-[#0442BF]' : 'text-[#FF7301]'}>
-              {isHumanBook ? ROLE_NAME[Role.HUBER] : ROLE_NAME[Role.LIBER]}
+            <p
+              className={`ml-3 text-xs ${
+                isHumanBook ? 'text-[#DBAE0A]' : 'text-[#0442BF]'
+              }`}
+            >
+              {isHumanBook ? ROLE_NAME[Role.LIBER] : ROLE_NAME[Role.HUBER]}
             </p>
           </div>
         </div>
@@ -183,7 +189,7 @@ export default function BigCalendar() {
             slotLabelContent={slotLabelContent}
             height="auto"
             contentHeight="auto"
-            slotMinTime="00:00:00"
+            slotMinTime="6:00:00"
             slotMaxTime="24:00:00"
             slotDuration="01:00:00"
             dayHeaderContent={dayHeaderContent}
diff --git a/src/components/schedule/components/BigSchedule/bigSchedule.css b/src/components/schedule/components/BigSchedule/bigSchedule.css
index 65d3dbe6..39fe0878 100644
--- a/src/components/schedule/components/BigSchedule/bigSchedule.css
+++ b/src/components/schedule/components/BigSchedule/bigSchedule.css
@@ -190,7 +190,6 @@ table table td:first-child .fc-timegrid-col-frame {
 }
 
 .calendar-scroll-wrapper {
-  max-height: calc(100vh - 200px);
   overflow-y: auto;
   overflow-x: visible;
   width: 100%;
@@ -209,3 +208,7 @@ table table td:first-child .fc-timegrid-col-frame {
   background: #d1d5db;
   border-radius: 8px;
 }
+
+.fc-timegrid-slot {
+  height: 140px !important;
+}
\ No newline at end of file
diff --git a/src/components/schedule/components/TimeSlot.tsx b/src/components/schedule/components/TimeSlot.tsx
index 35407d0f..e9d43341 100644
--- a/src/components/schedule/components/TimeSlot.tsx
+++ b/src/components/schedule/components/TimeSlot.tsx
@@ -40,6 +40,15 @@ function TimeSlot({ timeSlots }: { timeSlots: any }) {
     setIsModalOpen(true);
   };
 
+  const sortTimeSlots = (slots: any[]) => {
+    return slots.sort((a: any, b: any) => {
+      const [aHours, aMinutes] = a.startTime.split(':').map(Number);
+      const [bHours, bMinutes] = b.startTime.split(':').map(Number);
+      const aTime = aHours * 60 + aMinutes;
+      const bTime = bHours * 60 + bMinutes;
+      return aTime - bTime;
+    });
+  };
   const submitUpdateSlots = async () => {
     try {
       const sanitizedListDay = Object.entries(listDay).flatMap(([_, slots]) =>
@@ -196,6 +205,10 @@ function TimeSlot({ timeSlots }: { timeSlots: any }) {
     }
   }, [timeSlots]);
 
+  const hasMorningSlots = (listDay[currentDate]?.morning?.length || 0) > 0;
+  const hasAfternoonSlots = (listDay[currentDate]?.afternoon?.length || 0) > 0;
+  const hasEveningSlots = (listDay[currentDate]?.evening?.length || 0) > 0;
+
   return (
     <div className="mb-[20px] flex flex-col justify-center rounded-[12px] bg-[#fff] p-[16px] drop-shadow-md">
       <div className="my-[10px]">
@@ -216,50 +229,55 @@ function TimeSlot({ timeSlots }: { timeSlots: any }) {
             listDay[currentDate]?.evening,
           ) || []
         }
+        availableSlot
       />
       <div>
-        <div className="mb-[10px] flex flex-wrap items-center justify-start gap-[4px] rounded-[8px] bg-[#F9F9F9] p-[8px]">
-          {(listDay[currentDate] as { morning: any[] })?.morning?.length > 0 &&
-            (listDay[currentDate] as { morning: any[] }).morning.map(
-              (time: any, index: any) => (
-                <div
-                  key={index}
-                  className="my-[2px] flex h-[20px] w-[calc((100%/6)-4px)] items-center justify-center rounded-[16px] bg-[#D9F9CF] text-[8px] text-[#2A8010]"
-                >
-                  {time.startTime}
-                </div>
-              ),
-            )}
-        </div>
-        <div className="mb-[10px] flex flex-wrap items-center justify-start gap-[4px] rounded-[8px] bg-[#F9F9F9] p-[8px]">
-          {(listDay[currentDate] as { afternoon: any[] })?.afternoon?.length >
-            0 &&
-            (listDay[currentDate] as { afternoon: any[] }).afternoon.map(
-              (time: any, index: any) => (
-                <div
-                  key={index}
-                  className="my-[2px] flex h-[20px] w-[calc((100%/6)-4px)] items-center justify-center rounded-[16px] bg-[#D9F9CF] text-[8px] text-[#2A8010]"
-                >
-                  {time.startTime}
-                </div>
-              ),
-            )}
-        </div>
-        <div className="mb-[10px] flex flex-wrap items-center justify-start gap-[4px] rounded-[8px] bg-[#F9F9F9] p-[8px]">
-          {(listDay[currentDate] as { evening: any[] })?.evening?.length > 0 &&
-            (listDay[currentDate] as { evening: any[] }).evening.map(
-              (time: any, index: any) => (
-                <div
-                  key={index}
-                  className="my-[2px] flex h-[20px] w-[calc((100%/6)-4px)] items-center justify-center rounded-[16px] bg-[#D9F9CF] text-[8px] text-[#2A8010]"
-                >
-                  {time?.startTime}
-                </div>
-              ),
-            )}
-        </div>
+        {hasMorningSlots && (
+          <div className="mb-[10px] flex flex-wrap items-center justify-start gap-[4px] rounded-[8px] bg-[#F9F9F9] p-[8px]">
+            {sortTimeSlots(
+              (listDay[currentDate] as { morning: any[] }).morning,
+            ).map((time: any, index: any) => (
+              <div
+                key={index}
+                className="my-[2px] flex h-[20px] w-[calc((100%/6)-4px)] items-center justify-center rounded-[16px] bg-[#D9F9CF] text-[8px] text-[#2A8010]"
+              >
+                {time.startTime}
+              </div>
+            ))}
+          </div>
+        )}
+
+        {hasAfternoonSlots && (
+          <div className="mb-[10px] flex flex-wrap items-center justify-start gap-[4px] rounded-[8px] bg-[#F9F9F9] p-[8px]">
+            {sortTimeSlots(
+              (listDay[currentDate] as { afternoon: any[] }).afternoon,
+            ).map((time: any, index: any) => (
+              <div
+                key={index}
+                className="my-[2px] flex h-[20px] w-[calc((100%/6)-4px)] items-center justify-center rounded-[16px] bg-[#D9F9CF] text-[8px] text-[#2A8010]"
+              >
+                {time.startTime}
+              </div>
+            ))}
+          </div>
+        )}
+
+        {hasEveningSlots && (
+          <div className="mb-[10px] flex flex-wrap items-center justify-start gap-[4px] rounded-[8px] bg-[#F9F9F9] p-[8px]">
+            {sortTimeSlots(
+              (listDay[currentDate] as { evening: any[] }).evening,
+            ).map((time: any, index: any) => (
+              <div
+                key={index}
+                className="my-[2px] flex h-[20px] w-[calc((100%/6)-4px)] items-center justify-center rounded-[16px] bg-[#D9F9CF] text-[8px] text-[#2A8010]"
+              >
+                {time?.startTime}
+              </div>
+            ))}
+          </div>
+        )}
       </div>
-      <div className="w-full">
+      <div className="mt-2 w-full">
         <button
           type="button"
           onClick={handleUpdateTimeSlot}
diff --git a/src/components/schedule/components/sessionCard/PortalSessionCard.tsx b/src/components/schedule/components/sessionCard/PortalSessionCard.tsx
index b5c4e72a..38b80ddf 100644
--- a/src/components/schedule/components/sessionCard/PortalSessionCard.tsx
+++ b/src/components/schedule/components/sessionCard/PortalSessionCard.tsx
@@ -1,4 +1,4 @@
-import React, { useEffect, useRef } from 'react';
+import React, { useEffect, useRef, useState } from 'react';
 import { createPortal } from 'react-dom';
 
 import { SessionCard } from './SessionCard';
@@ -17,6 +17,28 @@ export const PortalSessionCard: React.FC<PortalSessionCardProps> = ({
   onClose,
 }) => {
   const cardRef = useRef<HTMLDivElement>(null);
+  const [isMobile, setIsMobile] = useState(false);
+  const [cardDimensions, setCardDimensions] = useState({ width: 0, height: 0 });
+
+  useEffect(() => {
+    const checkMobile = () => {
+      setIsMobile(window.innerWidth < 800);
+    };
+
+    checkMobile();
+    window.addEventListener('resize', checkMobile);
+
+    return () => {
+      window.removeEventListener('resize', checkMobile);
+    };
+  }, []);
+
+  useEffect(() => {
+    if (cardRef.current) {
+      const { offsetWidth, offsetHeight } = cardRef.current;
+      setCardDimensions({ width: offsetWidth, height: offsetHeight });
+    }
+  }, [session]);
 
   useEffect(() => {
     const handleClickOutside = (event: MouseEvent) => {
@@ -37,17 +59,56 @@ export const PortalSessionCard: React.FC<PortalSessionCardProps> = ({
 
   if (!session) return null;
 
-  return createPortal(
-    <div
-      ref={cardRef}
-      style={{
+  const getCardStyle = () => {
+    if (isMobile) {
+      return {
         position: 'fixed',
-        top: position.top,
-        left: position.left,
+        top: '50%',
+        left: '50%',
+        transform: 'translate(-50%, -50%)',
         zIndex: 9999,
         background: 'transparent',
-      }}
-      onMouseLeave={onClose}
+        maxWidth: '90%',
+        maxHeight: '80%',
+        overflow: 'auto',
+      };
+    }
+    const CARD_WIDTH = 400;
+    const CARD_HEIGHT = cardDimensions.height || 300;
+    const PADDING = 20;
+
+    const viewportWidth = window.innerWidth;
+    const viewportHeight = window.innerHeight;
+
+    let adjustedLeft = position.left;
+    let adjustedTop = position.top;
+
+    if (adjustedLeft < PADDING) {
+      adjustedLeft = PADDING;
+    }
+
+    if (adjustedLeft + CARD_WIDTH + PADDING > viewportWidth) {
+      adjustedLeft = Math.max(PADDING, viewportWidth - CARD_WIDTH - PADDING);
+    }
+
+    if (position.top + CARD_HEIGHT + PADDING > viewportHeight) {
+      adjustedTop = Math.max(PADDING, viewportHeight - CARD_HEIGHT - PADDING);
+    }
+
+    return {
+      position: 'fixed',
+      top: adjustedTop,
+      left: adjustedLeft,
+      zIndex: 9999,
+      background: 'transparent',
+    };
+  };
+
+  return createPortal(
+    <div
+      ref={cardRef}
+      style={getCardStyle() as React.CSSProperties}
+      onMouseLeave={isMobile ? undefined : onClose}
     >
       <SessionCard session={session} expanded={expanded} />
     </div>,
diff --git a/src/components/schedule/components/sessionCard/SessionActions.tsx b/src/components/schedule/components/sessionCard/SessionActions.tsx
index cd464a60..1377ddb0 100644
--- a/src/components/schedule/components/sessionCard/SessionActions.tsx
+++ b/src/components/schedule/components/sessionCard/SessionActions.tsx
@@ -1,3 +1,4 @@
+import { useTranslations } from 'next-intl';
 import React, { useState } from 'react';
 
 import { pushError, pushSuccess } from '@/components/CustomToastifyContainer';
@@ -20,42 +21,49 @@ export const SessionActions: React.FC<SessionActionsProps> = ({
 }) => {
   const [updateStatus, { isLoading }] = useUpdateStatusReadingSessionMutation();
   const [showCancelDialog, setShowCancelDialog] = useState(false);
-  const [cancelReason, setCancelReason] = useState('');
+  const t = useTranslations('Meeting');
+  const defaultCancelReason = t('cancel_meeting');
+  const [cancelReason, setCancelReason] = useState(defaultCancelReason);
 
   const handleStatusChange = async (newStatus: StatusType, reason?: string) => {
     try {
-      await updateStatus({
+      const payload: any = {
         id: sessionId,
         sessionStatus: newStatus,
-        ...(reason && { note: reason }),
-      }).unwrap();
+      };
+
+      if (newStatus === StatusEnum.Canceled) {
+        const finalReason = reason || defaultCancelReason;
+        payload.note = finalReason;
+      }
+
+      await updateStatus(payload).unwrap();
 
       if (onStatusChange) {
         onStatusChange(newStatus);
       }
       pushSuccess('Status updated successfully!');
 
-      // Reset cancel dialog state if it was a cancellation
       if (newStatus === StatusEnum.Canceled) {
         setShowCancelDialog(false);
-        setCancelReason('');
+        setCancelReason(defaultCancelReason);
       }
     } catch (error) {
       pushError('Failed to update status. Please try again.');
     }
   };
-
   const handleCancelRequest = () => {
     setShowCancelDialog(true);
   };
 
   const handleCancelConfirm = () => {
-    handleStatusChange(StatusEnum.Canceled, cancelReason);
+    const reasonToSend = cancelReason.trim() || defaultCancelReason;
+    handleStatusChange(StatusEnum.Canceled, reasonToSend);
   };
 
   const handleCancelAbort = () => {
     setShowCancelDialog(false);
-    setCancelReason('');
+    setCancelReason(defaultCancelReason);
   };
 
   const renderCancelDialog = () => {
@@ -70,8 +78,9 @@ export const SessionActions: React.FC<SessionActionsProps> = ({
             <textarea
               id="cancelReason"
               name="cancelReason"
+              required
               className="mt-1 w-full rounded-md border border-gray-300 p-2 text-sm"
-              placeholder="I don't want to meeting anymore"
+              placeholder={defaultCancelReason}
               value={cancelReason}
               onChange={(e) => setCancelReason(e.target.value)}
               rows={3}
diff --git a/src/components/schedule/components/sessionCard/SessionCard.tsx b/src/components/schedule/components/sessionCard/SessionCard.tsx
index da21ae73..d439caf5 100644
--- a/src/components/schedule/components/sessionCard/SessionCard.tsx
+++ b/src/components/schedule/components/sessionCard/SessionCard.tsx
@@ -121,8 +121,9 @@ export const SessionCard: React.FC<SessionCardProps> = ({
         </div>
       )}
       <SessionDateTime
-        startDate={session.startedAt}
-        endDate={session.endedAt}
+        date={session.startedAt}
+        startDate={session.startTime}
+        endDate={session.endTime}
       />
       {session.sessionUrl && session.sessionStatus !== StatusEnum.Pending && (
         <SessionUrl url={session.sessionUrl} />
diff --git a/src/components/schedule/components/sessionCard/SessionDateTime.tsx b/src/components/schedule/components/sessionCard/SessionDateTime.tsx
index 48484573..b1e09d9f 100644
--- a/src/components/schedule/components/sessionCard/SessionDateTime.tsx
+++ b/src/components/schedule/components/sessionCard/SessionDateTime.tsx
@@ -2,18 +2,20 @@ import { Calendar } from '@phosphor-icons/react';
 import React from 'react';
 
 interface SessionDateTimeProps {
+  date: string;
   startDate: string;
   endDate: string;
 }
 
 export const SessionDateTime: React.FC<SessionDateTimeProps> = ({
+  date,
   startDate,
   endDate,
 }) => {
   // Format date: convert ISO date string to readable format
   const formatDate = (dateString: string): string => {
-    const date = new Date(dateString);
-    return date.toLocaleDateString('en-GB', {
+    const parsedDate = new Date(dateString);
+    return parsedDate.toLocaleDateString('en-GB', {
       day: '2-digit',
       month: '2-digit',
       year: 'numeric',
@@ -22,27 +24,9 @@ export const SessionDateTime: React.FC<SessionDateTimeProps> = ({
 
   // Get day of week from date
   const getDayOfWeek = (dateString: string): string => {
-    const date = new Date(dateString);
+    const parsedDate = new Date(dateString);
     const days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
-    return days[date.getDay()] ?? '';
-  };
-
-  // Format time: convert ISO date string to readable time
-  const formatTime = (dateString: string): string => {
-    const date = new Date(dateString);
-    return date.toLocaleTimeString('en-GB', {
-      hour: '2-digit',
-      minute: '2-digit',
-      hour12: false,
-    });
-  };
-
-  // Calculate session duration
-  const getSessionDuration = (): string => {
-    if (!startDate || !endDate) return '';
-    const startTime = formatTime(startDate);
-    const endTime = formatTime(endDate);
-    return `${startTime} - ${endTime}`;
+    return days[parsedDate.getDay()] ?? '';
   };
 
   return (
@@ -50,9 +34,11 @@ export const SessionDateTime: React.FC<SessionDateTimeProps> = ({
       <Calendar size={16} className="mr-2" />
       <div>
         <span>
-          {getDayOfWeek(startDate)}, {formatDate(startDate)} |{' '}
+          {getDayOfWeek(date)}, {formatDate(date)} |{' '}
+        </span>
+        <span>
+          {startDate} - {endDate}
         </span>
-        <span>{getSessionDuration()}</span>
       </div>
     </div>
   );
diff --git a/src/libs/services/modules/reading-session/updateStatusReadingSession.ts b/src/libs/services/modules/reading-session/updateStatusReadingSession.ts
index fe590db4..5e288145 100644
--- a/src/libs/services/modules/reading-session/updateStatusReadingSession.ts
+++ b/src/libs/services/modules/reading-session/updateStatusReadingSession.ts
@@ -14,7 +14,7 @@ const updateStatusReadingSession = (
       note: string;
     }
   >({
-    query: ({ id, sessionStatus }) => ({
+    query: ({ id, sessionStatus, note }) => ({
       url: `reading-sessions/${id}`,
       method: 'PATCH',
       headers: {
@@ -22,6 +22,7 @@ const updateStatusReadingSession = (
       },
       body: JSON.stringify({
         sessionStatus,
+        note,
       }),
     }),
     invalidatesTags: [{ type: 'ReadingSession' }],
diff --git a/src/locales/en.json b/src/locales/en.json
index 56046d7c..e9ab0f18 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -430,6 +430,9 @@
     "error_time_slots": "An error has occurred. Please check your time slots and try again.",
     "success_time_slots_description": "You have successfully created your time slots"
   },
+  "Meeting": {
+    "cancel_meeting": "I don't want to meeting anymore"
+  },
   "Home": {
     "banner": {
       "title": "Hi!",
diff --git a/src/locales/vi.json b/src/locales/vi.json
index 983b13f0..aa8890a4 100644
--- a/src/locales/vi.json
+++ b/src/locales/vi.json
@@ -429,6 +429,9 @@
     "error_time_slots": "Đã xảy ra lỗi. Vui lòng kiểm tra khung giờ của bạn và thử lại.",
     "success_time_slots_description": "Bạn đã tạo khung giờ thành công"
   },
+  "Meeting": {
+    "cancel_meeting": "Tôi không muốn gặp nữa"
+  },
   "Home": {
     "banner": {
       "title": "Xin chào!",


Reviews:
user: login: HangNhung
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-25T04:26:54Z

Review comments:
diff_hunk: @@ -90,15 +93,23 @@ export default function HeadSlots({
 
       {/* Message below the day selector */}
       <Typography
-        className="mt-3 text-center"
+        className={`mt-3 ${
+          availableSlot ? 'text-left text-xs' : 'text-center'
+        }`}
         sx={{
-          color: numberOfTimeSlots > 0 ? '#38AA16' : '#e83e8c',
-          fontSize: '1.1rem',
+          color: availableSlot
+            ? '#38AA16'
+            : numberOfTimeSlots > 0
+              ? '#38AA16'
+              : '#e83e8c',
+          fontSize: availableSlot ? '0.75rem' : '1.1rem',
         }}
       >
         {numberOfTimeSlots > 0
           ? `Amazing!!! You can meet ${numberOfTimeSlots} Libers every ${days[selectedDay].name}! 💚`
-          : days[selectedDay].text}
+          : availableSlot
+            ? 'You did not choose any available time slot in this day, update now to meet with more people'
+            : days[selectedDay].text}
path: src/components/common/HeadSlots/index.tsx
user: login: HangNhung
body: bổ sung translate
created_at: 2025-05-25T04:07:08Z
author_association: CONTRIBUTOR
diff_hunk: @@ -15,7 +15,7 @@ const WeeklySchedule = () => {
   const isHuber = user?.role?.name === 'Huber';
   const { data: timeSlotHuber, isLoading: isLoadingTimeSlots } =
     useGetTimeSlotsHuberQuery(
-      { id: 23 },
+      { id: 51 },
path: src/components/schedule/WeeklySchedule.tsx
user: login: HangNhung
body: user.id
created_at: 2025-05-25T04:07:22Z
author_association: CONTRIBUTOR
diff_hunk: @@ -95,20 +95,22 @@ export default function BigCalendar() {
 
     return (
       <div
-        className="group relative z-[50] cursor-pointer overflow-visible"
+        className="group relative z-[50] h-[68px] cursor-pointer overflow-visible"
         onMouseEnter={(e) => handleMouseEnter(e, extendedProps)}
         onMouseLeave={handleMouseLeave}
       >
-        <div className="relative min-w-[60px] overflow-visible">
+        <div className="relative h-full min-w-[60px] overflow-visible">
           <div
-            className={`relative flex flex-col justify-start overflow-visible rounded-md border border-[#fff] ${
-              isHumanBook ? 'bg-[#CDDDFE]' : 'bg-[#FFE3CC]'
+            className={`relative flex h-full flex-col  justify-center overflow-visible rounded-xl border  ${
+              isHumanBook
+                ? 'border-[#FEF3C7] bg-[#FFFBEB]'
+                : 'border-[#DBEAFE] bg-[#DBEAFE]'
             } p-[2px]`}
           >
             {isPending && (
-              <p className="absolute left-[-20px] top-[-20px] flex h-[24px] w-[82px] items-center justify-center rounded-[100px] border-l-[#fff] bg-[#FFC745] p-[7px] text-[14px] font-[500] leading-[16px] text-[#000]">
-                Pending...
-              </p>
+              <span className=" inline-block h-[24px] w-[80px] self-end rounded-[100px] border-l-[#fff] bg-[#FFEDD5] p-[7px] text-right text-[12px] font-[500] leading-[16px] text-[#F97316]">
+                Waiting...
+              </span>
path: src/components/schedule/components/BigSchedule/BigSchedule.tsx
user: login: HangNhung
body: check lại tailwind config, mấy màu ko có hard code, khó maintain
Ví dụ figma có #FDF3CE => bg-yellow-90
chị check màu không đúng với figma, border chưa có luôn

created_at: 2025-05-25T04:11:49Z
author_association: CONTRIBUTOR
diff_hunk: @@ -196,6 +205,10 @@ function TimeSlot({ timeSlots }: { timeSlots: any }) {
     }
   }, [timeSlots]);
 
+  const hasMorningSlots = (listDay[currentDate]?.morning?.length || 0) > 0;
+  const hasAfternoonSlots = (listDay[currentDate]?.afternoon?.length || 0) > 0;
+  const hasEveningSlots = (listDay[currentDate]?.evening?.length || 0) > 0;
+
path: src/components/schedule/components/TimeSlot.tsx
user: login: HangNhung
body: dùng useMemo
created_at: 2025-05-25T04:12:30Z
author_association: CONTRIBUTOR
diff_hunk: @@ -216,50 +229,55 @@ function TimeSlot({ timeSlots }: { timeSlots: any }) {
             listDay[currentDate]?.evening,
           ) || []
         }
+        availableSlot
       />
       <div>
-        <div className="mb-[10px] flex flex-wrap items-center justify-start gap-[4px] rounded-[8px] bg-[#F9F9F9] p-[8px]">
-          {(listDay[currentDate] as { morning: any[] })?.morning?.length > 0 &&
-            (listDay[currentDate] as { morning: any[] }).morning.map(
-              (time: any, index: any) => (
-                <div
-                  key={index}
-                  className="my-[2px] flex h-[20px] w-[calc((100%/6)-4px)] items-center justify-center rounded-[16px] bg-[#D9F9CF] text-[8px] text-[#2A8010]"
-                >
-                  {time.startTime}
-                </div>
-              ),
-            )}
-        </div>
-        <div className="mb-[10px] flex flex-wrap items-center justify-start gap-[4px] rounded-[8px] bg-[#F9F9F9] p-[8px]">
-          {(listDay[currentDate] as { afternoon: any[] })?.afternoon?.length >
-            0 &&
-            (listDay[currentDate] as { afternoon: any[] }).afternoon.map(
-              (time: any, index: any) => (
-                <div
-                  key={index}
-                  className="my-[2px] flex h-[20px] w-[calc((100%/6)-4px)] items-center justify-center rounded-[16px] bg-[#D9F9CF] text-[8px] text-[#2A8010]"
-                >
-                  {time.startTime}
-                </div>
-              ),
-            )}
-        </div>
-        <div className="mb-[10px] flex flex-wrap items-center justify-start gap-[4px] rounded-[8px] bg-[#F9F9F9] p-[8px]">
-          {(listDay[currentDate] as { evening: any[] })?.evening?.length > 0 &&
-            (listDay[currentDate] as { evening: any[] }).evening.map(
-              (time: any, index: any) => (
-                <div
-                  key={index}
-                  className="my-[2px] flex h-[20px] w-[calc((100%/6)-4px)] items-center justify-center rounded-[16px] bg-[#D9F9CF] text-[8px] text-[#2A8010]"
-                >
-                  {time?.startTime}
-                </div>
-              ),
-            )}
-        </div>
+        {hasMorningSlots && (
+          <div className="mb-[10px] flex flex-wrap items-center justify-start gap-[4px] rounded-[8px] bg-[#F9F9F9] p-[8px]">
+            {sortTimeSlots(
+              (listDay[currentDate] as { morning: any[] }).morning,
+            ).map((time: any, index: any) => (
+              <div
+                key={index}
+                className="my-[2px] flex h-[20px] w-[calc((100%/6)-4px)] items-center justify-center rounded-[16px] bg-[#D9F9CF] text-[8px] text-[#2A8010]"
+              >
+                {time.startTime}
+              </div>
+            ))}
+          </div>
path: src/components/schedule/components/TimeSlot.tsx
user: login: HangNhung
body: define component => đang loop code
created_at: 2025-05-25T04:13:08Z
author_association: CONTRIBUTOR
diff_hunk: @@ -17,6 +17,28 @@ export const PortalSessionCard: React.FC<PortalSessionCardProps> = ({
   onClose,
 }) => {
   const cardRef = useRef<HTMLDivElement>(null);
+  const [isMobile, setIsMobile] = useState(false);
+  const [cardDimensions, setCardDimensions] = useState({ width: 0, height: 0 });
path: src/components/schedule/components/sessionCard/PortalSessionCard.tsx
user: login: HangNhung
body: check useDeviceType hook
created_at: 2025-05-25T04:16:57Z
author_association: CONTRIBUTOR

PR comments:
user: login: vercel[bot]
created_at: 2025-05-21T17:33:26Z
body: [vc]: #Zca/c8oD5/ZNuFZIt87ogyIcteq7Hxec+tLIuSMsAGY=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJodWxpYi1mZSIsInJvb3REaXJlY3RvcnkiOm51bGwsImluc3BlY3RvclVybCI6Imh0dHBzOi8vdmVyY2VsLmNvbS9odS1saWIvaHVsaWItZmUvNlF0OHZGS2dXRUNZWEFzZUNWWXo3alVQa3VZZSIsInByZXZpZXdVcmwiOiJodWxpYi1mZS1naXQtZml4LXNlc3Npb24taHUtbGliLnZlcmNlbC5hcHAiLCJuZXh0Q29tbWl0U3RhdHVzIjoiREVQTE9ZRUQiLCJsaXZlRmVlZGJhY2siOnsicmVzb2x2ZWQiOjAsInVucmVzb2x2ZWQiOjAsInRvdGFsIjowLCJsaW5rIjoiaHVsaWItZmUtZ2l0LWZpeC1zZXNzaW9uLWh1LWxpYi52ZXJjZWwuYXBwIn19XX0=
**The latest updates on your projects**. Learn more about [Vercel for Git ↗︎](https://vercel.link/github-learn-more)

| Name | Status | Preview | Comments | Updated (UTC) |
| :--- | :----- | :------ | :------- | :------ |
| **hulib-fe** | ✅ Ready ([Inspect](https://vercel.com/hu-lib/hulib-fe/6Qt8vFKgWECYXAseCVYz7jUPkuYe)) | [Visit Preview](https://hulib-fe-git-fix-session-hu-lib.vercel.app) | 💬 [**Add feedback**](https://vercel.live/open-feedback/hulib-fe-git-fix-session-hu-lib.vercel.app?via=pr-comment-feedback-link) | May 26, 2025 11:46am |


author_association: NONE
user: login: HangNhung
created_at: 2025-05-25T04:06:44Z
body: <img width="141" alt="image" src="https://github.com/user-attachments/assets/96b537af-387d-48a1-98c3-16f4b9af303c" />

author_association: CONTRIBUTOR
user: login: hulib-engineering
created_at: 2025-05-26T12:00:16Z
body: :tada: This PR is included in version 1.16.0 :tada:

The release is available on [GitHub release](https://github.com/hulib-engineering/hulib/releases/tag/v1.16.0)

Your **[semantic-release](https://github.com/semantic-release/semantic-release)** bot :package::rocket:
author_association: OWNER