Skip to content

Commit

Permalink
fix(media-transcoding): improve responsive styles (#2550)
Browse files Browse the repository at this point in the history
* fix(media-transcoding): improve responsive styles

* fix: typecheck issue
  • Loading branch information
Meierschlumpf authored Mar 9, 2025
1 parent d584ade commit 8f8d788
Show file tree
Hide file tree
Showing 7 changed files with 224 additions and 229 deletions.
8 changes: 4 additions & 4 deletions packages/translation/src/lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1890,10 +1890,10 @@
"statistics": {
"empty": "Empty",
"transcodes": "Transcodes",
"transcodesCount": "Transcodes: {value}",
"healthChecksCount": "Health checks: {value}",
"filesCount": "Files: {value}",
"savedSpace": "Saved space: {value}",
"transcodesCount": "Transcodes",
"healthChecksCount": "Health checks",
"filesCount": "Files",
"savedSpace": "Saved space",
"healthChecks": "Health checks",
"videoCodecs": "Codecs",
"videoContainers": "Containers",
Expand Down
108 changes: 52 additions & 56 deletions packages/widgets/src/media-transcoding/component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,32 @@

import { useState } from "react";
import { Center, Divider, Group, Pagination, SegmentedControl, Stack, Text } from "@mantine/core";
import type { TablerIcon } from "@tabler/icons-react";
import { IconClipboardList, IconCpu2, IconReportAnalytics } from "@tabler/icons-react";

import { clientApi } from "@homarr/api/client";
import { useI18n } from "@homarr/translation/client";

import { views } from ".";
import type { WidgetComponentProps } from "../definition";
import { HealthCheckStatus } from "./health-check-status";
import { QueuePanel } from "./panels/queue.panel";
import { StatisticsPanel } from "./panels/statistics.panel";
import { WorkersPanel } from "./panels/workers.panel";

type Views = "workers" | "queue" | "statistics";
type View = (typeof views)[number];

export default function MediaTranscodingWidget({ integrationIds, options }: WidgetComponentProps<"mediaTranscoding">) {
const viewIcons = {
workers: IconCpu2,
queue: IconClipboardList,
statistics: IconReportAnalytics,
} satisfies Record<View, TablerIcon>;

export default function MediaTranscodingWidget({
integrationIds,
options,
width,
}: WidgetComponentProps<"mediaTranscoding">) {
const [queuePage, setQueuePage] = useState(1);
const queuePageSize = 10;
const [transcodingData] = clientApi.widget.mediaTranscoding.getDataAsync.useSuspenseQuery(
Expand All @@ -31,15 +43,16 @@ export default function MediaTranscodingWidget({ integrationIds, options }: Widg
},
);

const [view, setView] = useState<Views>(options.defaultView);
const [view, setView] = useState<View>(options.defaultView);
const totalQueuePages = Math.ceil((transcodingData.data.queue.totalCount || 1) / queuePageSize);

const t = useI18n("widget.mediaTranscoding");
const isTiny = width < 256;

return (
<Stack gap={4} h="100%">
{view === "workers" ? (
<WorkersPanel workers={transcodingData.data.workers} />
<WorkersPanel workers={transcodingData.data.workers} isTiny={isTiny} />
) : view === "queue" ? (
<QueuePanel queue={transcodingData.data.queue} />
) : (
Expand All @@ -48,65 +61,48 @@ export default function MediaTranscodingWidget({ integrationIds, options }: Widg
<Divider />
<Group gap="xs" mb={4} ms={4} me={8}>
<SegmentedControl
data={[
{
data={views.map((value) => {
const Icon = viewIcons[value];
return {
label: (
<Center>
<IconCpu2 size={18} />
<Text size="xs" ml={8}>
{t("tab.workers")}
</Text>
<Center style={{ gap: 4 }}>
<Icon size={12} />
{!isTiny && (
<Text span size="xs">
{t(`tab.${value}`)}
</Text>
)}
</Center>
),
value: "workers",
},
{
label: (
<Center>
<IconClipboardList size={18} />
<Text size="xs" ml={8}>
{t("tab.queue")}
</Text>
</Center>
),
value: "queue",
},
{
label: (
<Center>
<IconReportAnalytics size={18} />
<Text size="xs" ml={8}>
{t("tab.statistics")}
</Text>
</Center>
),
value: "statistics",
},
]}
value,
};
})}
value={view}
onChange={(value) => setView(value as Views)}
onChange={(value) => setView(value as View)}
size="xs"
/>
{view === "queue" && (
<>
<Pagination.Root total={totalQueuePages} value={queuePage} onChange={setQueuePage} size="sm">
<Group gap={5} justify="center">
<Pagination.First disabled={transcodingData.data.queue.startIndex === 1} />
<Pagination.Previous disabled={transcodingData.data.queue.startIndex === 1} />
<Pagination.Next disabled={transcodingData.data.queue.startIndex === totalQueuePages} />
<Pagination.Last disabled={transcodingData.data.queue.startIndex === totalQueuePages} />
</Group>
</Pagination.Root>
<Text size="xs">
{t("currentIndex", {
start: transcodingData.data.queue.startIndex + 1,
end: transcodingData.data.queue.endIndex + 1,
total: transcodingData.data.queue.totalCount,
})}
</Text>
</>
)}

<Group gap="xs" ml="auto">
{view === "queue" && (
<>
<Pagination.Root total={totalQueuePages} value={queuePage} onChange={setQueuePage} size="xs">
<Group gap={2} justify="center">
{!isTiny && <Pagination.First disabled={transcodingData.data.queue.startIndex === 1} />}
<Pagination.Previous disabled={transcodingData.data.queue.startIndex === 1} />
<Pagination.Next disabled={transcodingData.data.queue.startIndex === totalQueuePages} />
{!isTiny && <Pagination.Last disabled={transcodingData.data.queue.startIndex === totalQueuePages} />}
</Group>
</Pagination.Root>
<Text size="xs">
{t("currentIndex", {
start: transcodingData.data.queue.startIndex + 1,
end: transcodingData.data.queue.endIndex + 1,
total: transcodingData.data.queue.totalCount,
})}
</Text>
</>
)}

<HealthCheckStatus statistics={transcodingData.data.statistics} />
</Group>
</Group>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ export function HealthCheckStatus(props: HealthCheckStatusProps) {
return (
<HoverCard position="bottom" width={250} shadow="sm">
<HoverCard.Target>
<Indicator color={textColor(indicatorColor, colorScheme)} size={8} display="flex">
<IconHeartbeat size={20} />
<Indicator color={textColor(indicatorColor, colorScheme)} size={6} display="flex">
<IconHeartbeat size={16} />
</Indicator>
</HoverCard.Target>
<HoverCard.Dropdown bg={colorScheme === "light" ? "gray.2" : "dark.8"}>
Expand Down
10 changes: 5 additions & 5 deletions packages/widgets/src/media-transcoding/index.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import { IconTransform } from "@tabler/icons-react";
import { z } from "zod";

import { capitalize } from "@homarr/common";

import { createWidgetDefinition } from "../definition";
import { optionsBuilder } from "../options";

export const views = ["workers", "queue", "statistics"] as const;

export const { componentLoader, definition } = createWidgetDefinition("mediaTranscoding", {
icon: IconTransform,
createOptions() {
return optionsBuilder.from((factory) => ({
defaultView: factory.select({
defaultValue: "statistics",
options: [
{ label: "Workers", value: "workers" },
{ label: "Queue", value: "queue" },
{ label: "Statistics", value: "statistics" },
],
options: views.map((view) => ({ label: capitalize(view), value: view })),
}),
queuePageSize: factory.number({ defaultValue: 10, validate: z.number().min(1).max(30) }),
}));
Expand Down
54 changes: 30 additions & 24 deletions packages/widgets/src/media-transcoding/panels/queue.panel.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Center, Group, ScrollArea, Table, Text, Title, Tooltip } from "@mantine/core";
import { Center, Group, ScrollArea, Table, TableTd, TableTh, TableTr, Text, Title, Tooltip } from "@mantine/core";
import { IconHeartbeat, IconTransform } from "@tabler/icons-react";

import { humanFileSize } from "@homarr/common";
Expand All @@ -17,7 +17,7 @@ export function QueuePanel(props: QueuePanelProps) {
if (queue.array.length === 0) {
return (
<Center style={{ flex: "1" }}>
<Title order={3}>{t("empty")}</Title>
<Title order={6}>{t("empty")}</Title>
</Center>
);
}
Expand All @@ -26,36 +26,42 @@ export function QueuePanel(props: QueuePanelProps) {
<ScrollArea style={{ flex: "1" }}>
<Table style={{ tableLayout: "fixed" }}>
<Table.Thead>
<tr>
<th>{t("table.file")}</th>
<th style={{ width: 80 }}>{t("table.size")}</th>
</tr>
<TableTr>
<TableTh ta="start" py={4}>
<Text size="xs" fw="bold">
{t("table.file")}
</Text>
</TableTh>
<TableTh ta="start" py={4}>
<Text size="xs" fw="bold">
{t("table.size")}
</Text>
</TableTh>
</TableTr>
</Table.Thead>
<Table.Tbody>
{queue.array.map((item) => (
<tr key={item.id}>
<td>
<Group gap="xs" wrap="nowrap">
<div>
{item.type === "transcode" ? (
<Tooltip label={t("table.transcode")}>
<IconTransform size={14} />
</Tooltip>
) : (
<Tooltip label={t("table.healthCheck")}>
<IconHeartbeat size={14} />
</Tooltip>
)}
</div>
<TableTr key={item.id}>
<TableTd py={2}>
<Group gap={4} wrap="nowrap">
{item.type === "transcode" ? (
<Tooltip label={t("table.transcode")}>
<IconTransform size={12} />
</Tooltip>
) : (
<Tooltip label={t("table.healthCheck")}>
<IconHeartbeat size={12} />
</Tooltip>
)}
<Text lineClamp={1} size="xs">
{item.filePath.split("\\").pop()?.split("/").pop() ?? item.filePath}
</Text>
</Group>
</td>
<td>
</TableTd>
<TableTd py={2}>
<Text size="xs">{humanFileSize(item.fileSize)}</Text>
</td>
</tr>
</TableTd>
</TableTr>
))}
</Table.Tbody>
</Table>
Expand Down
Loading

0 comments on commit 8f8d788

Please sign in to comment.