Explorar el Código

refactor: show placeholder image when history is empty (#398)

ayangweb hace 1 mes
padre
commit
77e6b58381

BIN
public/assets/no_data_dark.png


BIN
public/assets/no_data_light.png


+ 187 - 178
src/components/Common/HistoryList/index.tsx

@@ -19,6 +19,7 @@ import { useTranslation } from "react-i18next";
 
 import VisibleKey from "../VisibleKey";
 import { Chat } from "@/components/Assistant/types";
+import NoDataImage from "../NoDataImage";
 
 dayjs.extend(isSameOrAfter);
 
@@ -158,7 +159,7 @@ const HistoryList: FC<HistoryListProps> = (props) => {
       ref={listRef}
       id={id}
       className={clsx(
-        "h-full overflow-auto px-3 py-2 text-sm bg-[#F3F4F6] dark:bg-[#1F2937]"
+        "flex flex-col h-full overflow-auto px-3 py-2 text-sm bg-[#F3F4F6] dark:bg-[#1F2937]"
       )}
     >
       <div className="flex gap-1 children:h-8">
@@ -197,194 +198,202 @@ const HistoryList: FC<HistoryListProps> = (props) => {
         </div>
       </div>
 
-      <div className="mt-6">
-        {Object.entries(sortedList).map(([label, list]) => {
-          return (
-            <div key={label}>
-              <span className="text-xs text-[#999] px-3">{t(label)}</span>
-
-              <ul>
-                {list.map((item) => {
-                  const { _id, _source } = item;
-
-                  const isActive = _id === active?._id;
-                  const title = _source?.title ?? _id;
-
-                  return (
-                    <li
-                      key={_id}
-                      id={_id}
-                      className={clsx(
-                        "flex items-center mt-1 h-10 rounded-lg cursor-pointer hover:bg-[#EDEDED] dark:hover:bg-[#353F4D] transition",
-                        {
-                          "!bg-[#E5E7EB] dark:!bg-[#2B3444]": isActive,
-                        }
-                      )}
-                      onClick={() => {
-                        if (!isActive) {
-                          setIsEdit(false);
-                        }
-
-                        onSelect(item);
-                      }}
-                    >
-                      <div
-                        className={clsx("w-1 h-6 rounded-sm bg-[#0072FF]", {
-                          "opacity-0": _id !== active?._id,
-                        })}
-                      />
-
-                      <div className="flex-1 flex items-center justify-between gap-2 px-2 overflow-hidden">
-                        {isEdit && isActive ? (
-                          <Input
-                            autoFocus
-                            defaultValue={title}
-                            className="flex-1 -mx-px outline-none bg-transparent border border-[#0061FF] rounded-[4px]"
-                            onKeyDown={(event) => {
-                              if (event.key !== "Enter") return;
-
-                              onRename(item._id, event.currentTarget.value);
-
-                              setIsEdit(false);
-                            }}
-                            onBlur={(event) => {
-                              onRename(item._id, event.target.value);
-
+      {list.length > 0 ? (
+        <>
+          <div className="mt-6">
+            {Object.entries(sortedList).map(([label, list]) => {
+              return (
+                <div key={label}>
+                  <span className="text-xs text-[#999] px-3">{t(label)}</span>
+
+                  <ul>
+                    {list.map((item) => {
+                      const { _id, _source } = item;
+
+                      const isActive = _id === active?._id;
+                      const title = _source?.title ?? _id;
+
+                      return (
+                        <li
+                          key={_id}
+                          id={_id}
+                          className={clsx(
+                            "flex items-center mt-1 h-10 rounded-lg cursor-pointer hover:bg-[#EDEDED] dark:hover:bg-[#353F4D] transition",
+                            {
+                              "!bg-[#E5E7EB] dark:!bg-[#2B3444]": isActive,
+                            }
+                          )}
+                          onClick={() => {
+                            if (!isActive) {
                               setIsEdit(false);
-                            }}
+                            }
+
+                            onSelect(item);
+                          }}
+                        >
+                          <div
+                            className={clsx("w-1 h-6 rounded-sm bg-[#0072FF]", {
+                              "opacity-0": _id !== active?._id,
+                            })}
                           />
-                        ) : (
-                          <span className="truncate">{title}</span>
-                        )}
-
-                        <div className="flex items-center gap-2">
-                          {isActive && !isEdit && (
-                            <VisibleKey
-                              shortcut="↑↓"
-                              rootClassName="w-6"
-                              shortcutClassName="w-6"
-                            />
-                          )}
 
-                          <Popover>
-                            {isActive && !isEdit && (
-                              <PopoverButton
-                                ref={moreButtonRef}
-                                className="flex gap-2"
-                              >
-                                <VisibleKey
-                                  shortcut="O"
-                                  onKeyPress={() => {
-                                    moreButtonRef.current?.click();
-                                  }}
-                                >
-                                  <Ellipsis className="size-4 text-[#979797]" />
-                                </VisibleKey>
-                              </PopoverButton>
+                          <div className="flex-1 flex items-center justify-between gap-2 px-2 overflow-hidden">
+                            {isEdit && isActive ? (
+                              <Input
+                                autoFocus
+                                defaultValue={title}
+                                className="flex-1 -mx-px outline-none bg-transparent border border-[#0061FF] rounded-[4px]"
+                                onKeyDown={(event) => {
+                                  if (event.key !== "Enter") return;
+
+                                  onRename(item._id, event.currentTarget.value);
+
+                                  setIsEdit(false);
+                                }}
+                                onBlur={(event) => {
+                                  onRename(item._id, event.target.value);
+
+                                  setIsEdit(false);
+                                }}
+                              />
+                            ) : (
+                              <span className="truncate">{title}</span>
                             )}
 
-                            <PopoverPanel
-                              anchor="bottom"
-                              className="flex flex-col rounded-lg shadow-md z-100 bg-white dark:bg-[#202126] p-1 border border-black/2 dark:border-white/10"
-                              onClick={(event) => {
-                                event.stopPropagation();
-                              }}
-                            >
-                              {menuItems.map((menuItem) => {
-                                const {
-                                  label,
-                                  icon: Icon,
-                                  shortcut,
-                                  iconColor,
-                                  onClick,
-                                } = menuItem;
-
-                                return (
-                                  <button
-                                    key={label}
-                                    className="flex items-center gap-2 px-3 py-2 text-sm rounded-md hover:bg-[#EDEDED] dark:hover:bg-[#2B2C31] transition"
-                                    onClick={onClick}
+                            <div className="flex items-center gap-2">
+                              {isActive && !isEdit && (
+                                <VisibleKey
+                                  shortcut="↑↓"
+                                  rootClassName="w-6"
+                                  shortcutClassName="w-6"
+                                />
+                              )}
+
+                              <Popover>
+                                {isActive && !isEdit && (
+                                  <PopoverButton
+                                    ref={moreButtonRef}
+                                    className="flex gap-2"
                                   >
                                     <VisibleKey
-                                      shortcut={shortcut}
-                                      onKeyPress={onClick}
+                                      shortcut="O"
+                                      onKeyPress={() => {
+                                        moreButtonRef.current?.click();
+                                      }}
                                     >
-                                      <Icon
-                                        className="size-4"
-                                        style={{
-                                          color: iconColor,
-                                        }}
-                                      />
+                                      <Ellipsis className="size-4 text-[#979797]" />
                                     </VisibleKey>
-
-                                    <span>{t(label)}</span>
-                                  </button>
-                                );
-                              })}
-                            </PopoverPanel>
-                          </Popover>
-                        </div>
-                      </div>
-                    </li>
-                  );
-                })}
-              </ul>
-            </div>
-          );
-        })}
-      </div>
-
-      <Dialog
-        open={isOpen}
-        onClose={() => setIsOpen(false)}
-        className="relative z-1000"
-      >
-        <div
-          id="headlessui-popover-panel:delete-history"
-          className="fixed inset-0 flex items-center justify-center w-screen"
-        >
-          <DialogPanel className="flex flex-col justify-between w-[360px] h-[160px] p-3 text-[#333] dark:text-white/90 border border-[#e6e6e6] bg-white dark:bg-[#202126] dark:border-white/10 shadow-xl rounded-lg">
-            <div className="flex flex-col gap-3">
-              <DialogTitle className="text-base font-bold">
-                {t("history_list.delete_modal.title")}
-              </DialogTitle>
-              <Description className="text-sm">
-                {t("history_list.delete_modal.description", {
-                  replace: [active?._source?.title || active?._id],
-                })}
-              </Description>
-            </div>
-
-            <div className="flex gap-4 self-end">
-              <VisibleKey
-                shortcut="N"
-                shortcutClassName="left-[unset] right-0"
-                onKeyPress={() => setIsOpen(false)}
-              >
-                <button
-                  className="h-8 px-4 text-sm text-[#666666] bg-[#F8F9FA] dark:text-white dark:bg-[#202126] border border-[#E6E6E6] dark:border-white/10 rounded-lg"
-                  onClick={() => setIsOpen(false)}
-                >
-                  {t("history_list.delete_modal.button.cancel")}
-                </button>
-              </VisibleKey>
-
-              <VisibleKey
-                shortcut="Y"
-                shortcutClassName="left-[unset] right-0"
-                onKeyPress={handleRemove}
-              >
-                <button
-                  className="h-8 px-4 text-sm text-white bg-[#EF4444] rounded-lg"
-                  onClick={handleRemove}
-                >
-                  {t("history_list.delete_modal.button.delete")}
-                </button>
-              </VisibleKey>
+                                  </PopoverButton>
+                                )}
+
+                                <PopoverPanel
+                                  anchor="bottom"
+                                  className="flex flex-col rounded-lg shadow-md z-100 bg-white dark:bg-[#202126] p-1 border border-black/2 dark:border-white/10"
+                                  onClick={(event) => {
+                                    event.stopPropagation();
+                                  }}
+                                >
+                                  {menuItems.map((menuItem) => {
+                                    const {
+                                      label,
+                                      icon: Icon,
+                                      shortcut,
+                                      iconColor,
+                                      onClick,
+                                    } = menuItem;
+
+                                    return (
+                                      <button
+                                        key={label}
+                                        className="flex items-center gap-2 px-3 py-2 text-sm rounded-md hover:bg-[#EDEDED] dark:hover:bg-[#2B2C31] transition"
+                                        onClick={onClick}
+                                      >
+                                        <VisibleKey
+                                          shortcut={shortcut}
+                                          onKeyPress={onClick}
+                                        >
+                                          <Icon
+                                            className="size-4"
+                                            style={{
+                                              color: iconColor,
+                                            }}
+                                          />
+                                        </VisibleKey>
+
+                                        <span>{t(label)}</span>
+                                      </button>
+                                    );
+                                  })}
+                                </PopoverPanel>
+                              </Popover>
+                            </div>
+                          </div>
+                        </li>
+                      );
+                    })}
+                  </ul>
+                </div>
+              );
+            })}
+          </div>
+
+          <Dialog
+            open={isOpen}
+            onClose={() => setIsOpen(false)}
+            className="relative z-1000"
+          >
+            <div
+              id="headlessui-popover-panel:delete-history"
+              className="fixed inset-0 flex items-center justify-center w-screen"
+            >
+              <DialogPanel className="flex flex-col justify-between w-[360px] h-[160px] p-3 text-[#333] dark:text-white/90 border border-[#e6e6e6] bg-white dark:bg-[#202126] dark:border-white/10 shadow-xl rounded-lg">
+                <div className="flex flex-col gap-3">
+                  <DialogTitle className="text-base font-bold">
+                    {t("history_list.delete_modal.title")}
+                  </DialogTitle>
+                  <Description className="text-sm">
+                    {t("history_list.delete_modal.description", {
+                      replace: [active?._source?.title || active?._id],
+                    })}
+                  </Description>
+                </div>
+
+                <div className="flex gap-4 self-end">
+                  <VisibleKey
+                    shortcut="N"
+                    shortcutClassName="left-[unset] right-0"
+                    onKeyPress={() => setIsOpen(false)}
+                  >
+                    <button
+                      className="h-8 px-4 text-sm text-[#666666] bg-[#F8F9FA] dark:text-white dark:bg-[#202126] border border-[#E6E6E6] dark:border-white/10 rounded-lg"
+                      onClick={() => setIsOpen(false)}
+                    >
+                      {t("history_list.delete_modal.button.cancel")}
+                    </button>
+                  </VisibleKey>
+
+                  <VisibleKey
+                    shortcut="Y"
+                    shortcutClassName="left-[unset] right-0"
+                    onKeyPress={handleRemove}
+                  >
+                    <button
+                      className="h-8 px-4 text-sm text-white bg-[#EF4444] rounded-lg"
+                      onClick={handleRemove}
+                    >
+                      {t("history_list.delete_modal.button.delete")}
+                    </button>
+                  </VisibleKey>
+                </div>
+              </DialogPanel>
             </div>
-          </DialogPanel>
+          </Dialog>
+        </>
+      ) : (
+        <div className="flex items-center justify-center flex-1">
+          <NoDataImage />
         </div>
-      </Dialog>
+      )}
     </div>
   );
 };

+ 18 - 0
src/components/Common/NoDataImage.tsx

@@ -0,0 +1,18 @@
+import { useThemeStore } from "@/stores/themeStore";
+import clsx from "clsx";
+import { FC, HTMLAttributes } from "react";
+
+const NoDataImage: FC<HTMLAttributes<HTMLImageElement>> = (props) => {
+  const { className } = props;
+  const isDark = useThemeStore((state) => state.isDark);
+
+  return (
+    <img
+      {...props}
+      src={isDark ? "/assets/no_data_dark.png" : "/assets/no_data_light.png"}
+      className={clsx("size-16", className)}
+    />
+  );
+};
+
+export default NoDataImage;