teableio/teable

View on GitHub
apps/nextjs-app/src/features/app/blocks/graph/FieldGraph.tsx

Summary

Maintainability
A
0 mins
Test Coverage
import type { IFieldRo } from '@teable/core';
import { ColorUtils } from '@teable/core';
import { useLanDayjs } from '@teable/sdk/hooks';
import { Badge } from '@teable/ui-lib/shadcn';
import dayjs from 'dayjs';
import duration from 'dayjs/plugin/duration';
import relativeTime from 'dayjs/plugin/relativeTime';

import { useTranslation } from 'next-i18next';
import { useEffect, useRef, useState } from 'react';
import { useGraph } from './useGraph';
import { usePlan } from './usePlan';

dayjs.extend(duration);
dayjs.extend(relativeTime);

export const FieldGraph = (params: { tableId: string; fieldId?: string; fieldRo?: IFieldRo }) => {
  const ref = useRef(null);
  const planData = usePlan(params);
  const updateCellCount = planData?.updateCellCount;
  const estimateTime = planData?.estimateTime || 0;
  const { t, i18n } = useTranslation(['table']);
  const dayjs = useLanDayjs();

  const [tables, setTables] = useState<{ name: string; color: string }[]>([]);

  const { updateGraph } = useGraph(ref);

  useEffect(() => {
    const graph = planData?.graph;

    if (!graph) {
      return;
    }

    const { nodes, edges, combos } = graph;
    const cache: Record<string, string> = {};
    updateGraph({
      nodes: nodes?.map((node) => {
        const comboId = node.comboId || 'default';
        const color = cache[comboId] ? cache[comboId] : ColorUtils.getRandomColorFromStr(comboId);
        cache[comboId] = color;
        const stroke = ColorUtils.getHexForColor(color);
        return {
          ...node,
          label: `${node.label}`,
          style: {
            stroke,
            lineWidth: node.isSelected ? 5 : 1,
            lineDash: node.isSelected ? [6, 4] : undefined,
            fill: stroke,
          },
        };
      }),
      edges,
    });
    setTables(
      combos?.map((combo) => ({
        name: combo.label,
        color: ColorUtils.getHexForColor(cache[combo.id]) || '',
      }))
    );
  }, [planData, updateGraph]);

  const formatDuration = dayjs(Date.now() + estimateTime).fromNow();

  return (
    <div className="flex flex-col gap-2 pb-2">
      <div className="flex items-center gap-2 pb-2 text-sm">
        {t('table.graph.tableLabel')}
        {tables.map((table) => {
          return (
            <div
              key={table.color}
              className="flex items-center justify-center gap-1 rounded border px-1 py-[2px]"
              style={{
                borderColor: table.color,
                backgroundColor: table.color + '80',
              }}
            >
              <span>{table.name}</span>
            </div>
          );
        })}
      </div>
      <div className="flex items-center gap-2 text-xs">
        <div>
          {t('table.graph.effectCells')}:{' '}
          <Badge>{Intl.NumberFormat(i18n.language).format(updateCellCount || 0)}</Badge>
        </div>
        <div>
          {t('table.graph.estimatedTime')}: <b>{formatDuration}</b>
        </div>
      </div>
      <div className="relative flex h-[calc(100vh-400px)] max-h-[600px] w-full flex-col">
        <div ref={ref} className="grow rounded border shadow"></div>
      </div>
    </div>
  );
};