JFIFxxC      C  " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr{ gilour

File "notification-list.tsx"

Full Path: /home/markqprx/iniasli.pro/common/resources/client/notifications/notification-list.tsx
File size: 4.83 KB
MIME-type: text/plain
Charset: utf-8

import React, {JSXElementConstructor, useContext} from 'react';
import {GroupAddIcon} from '../icons/material/GroupAdd';
import {PeopleIcon} from '../icons/material/People';
import {FileDownloadDoneIcon} from '../icons/material/FileDownloadDone';
import {
  DatabaseNotification,
  DatabaseNotificationAction,
} from './database-notification';
import {MixedImage} from '../ui/images/mixed-image';
import {Button} from '../ui/buttons/button';
import {SiteConfigContext} from '../core/settings/site-config-context';
import {Line} from './notification-line';
import {SvgIconProps} from '../icons/svg-icon';
import clsx from 'clsx';
import {useMarkNotificationsAsRead} from './requests/use-mark-notifications-as-read';
import {useNavigate} from '../utils/hooks/use-navigate';
import {isAbsoluteUrl} from '../utils/urls/is-absolute-url';
import {Link} from 'react-router-dom';
import {useSettings} from '../core/settings/use-settings';

const iconMap = {
  'group-add': GroupAddIcon,
  people: PeopleIcon,
  'export-csv': FileDownloadDoneIcon,
} as Record<string, JSXElementConstructor<SvgIconProps>>;

interface NotificationListProps {
  notifications: DatabaseNotification[];
  className?: string;
}
export function NotificationList({
  notifications,
  className,
}: NotificationListProps) {
  const {notifications: config} = useContext(SiteConfigContext);

  return (
    <div className={className}>
      {notifications.map((notification, index) => {
        const isLast = notifications.length - 1 === index;
        const Renderer =
          config?.renderMap?.[notification.type] || NotificationListItem;
        return (
          <Renderer
            key={notification.id}
            notification={notification}
            isLast={isLast}
          />
        );
      })}
    </div>
  );
}

export interface NotificationListItemProps {
  notification: DatabaseNotification;
  onActionButtonClick?: ButtonActionsProps['onActionClick'];
  lineIconRenderer?: JSXElementConstructor<{icon: string}>;
  isLast: boolean;
}
export function NotificationListItem({
  notification,
  onActionButtonClick,
  lineIconRenderer,
  isLast,
}: NotificationListItemProps) {
  const markAsRead = useMarkNotificationsAsRead();
  const navigate = useNavigate();
  const mainAction = notification.data.mainAction;

  const showUnreadIndicator = !notification.data.image && !notification.read_at;

  return (
    <div
      onClick={() => {
        if (!markAsRead.isPending && !notification.read_at) {
          markAsRead.mutate({ids: [notification.id]});
        }
        if (mainAction?.action) {
          if (isAbsoluteUrl(mainAction.action)) {
            window.open(mainAction.action, '_blank')?.focus();
          } else {
            navigate(mainAction.action);
          }
        }
      }}
      className={clsx(
        'flex items-start gap-14 px-32 py-20 bg-alt relative',
        !isLast && 'border-b',
        mainAction?.action && 'cursor-pointer',
        !notification.read_at
          ? 'bg-paper hover:bg-primary/10'
          : 'hover:bg-hover',
      )}
      title={mainAction?.label ? mainAction.label : undefined}
    >
      {showUnreadIndicator && (
        <div className="absolute left-16 top-26 w-8 h-8 shadow rounded-full bg-primary flex-shrink-0" />
      )}
      {notification.data.image && (
        <MixedImage
          className="w-24 h-24 flex-shrink-0 text-muted"
          src={iconMap[notification.data.image] || notification.data.image}
        />
      )}
      <div className="min-w-0">
        {notification.data.lines.map((line, index) => (
          <Line
            iconRenderer={lineIconRenderer}
            notification={notification}
            line={line}
            index={index}
            key={index}
          />
        ))}
        <ButtonActions
          onActionClick={onActionButtonClick}
          notification={notification}
        />
      </div>
    </div>
  );
}

interface ButtonActionsProps {
  notification: DatabaseNotification;
  onActionClick?: (
    e: React.MouseEvent,
    action: DatabaseNotificationAction,
  ) => void;
}
function ButtonActions({notification, onActionClick}: ButtonActionsProps) {
  const {base_url} = useSettings();
  if (!notification.data.buttonActions) return null;

  // if there's no action handler provided, assume action is internal url and render a link
  return (
    <div className="mt-12 flex items-center gap-12">
      {notification.data.buttonActions.map((action, index) => (
        <Button
          key={index}
          size="xs"
          variant={index === 0 ? 'flat' : 'outline'}
          color={index === 0 ? 'primary' : null}
          elementType={!onActionClick ? Link : undefined}
          to={!onActionClick ? action.action.replace(base_url, '') : undefined}
          onClick={e => {
            onActionClick?.(e, action);
          }}
        >
          {action.label}
        </Button>
      ))}
    </div>
  );
}