Compare commits

..
Author SHA1 Message Date
Philipinho 29869478e6 fix: license display 2026-08-26 20:02:01 +01:00
6 changed files with 66 additions and 73 deletions
@@ -34,6 +34,7 @@ export default function LicenseDetails() {
<Table.Td>
{license.licenseType === "business" ? "Business" : "Enterprise"}{" "}
{license.trial && <Badge color="green">Trial</Badge>}
{license.lifetime && <Badge color="blue">Lifetime</Badge>}
</Table.Td>
</Table.Tr>
@@ -59,7 +60,14 @@ export default function LicenseDetails() {
<Table.Tr>
<Table.Th>Expires at</Table.Th>
<Table.Td>
{formatLocalized(license.expiresAt, "dd MMMM, yyyy", "PPP", locale)}
{license.lifetime
? "Never"
: formatLocalized(
license.expiresAt,
"dd MMMM, yyyy",
"PPP",
locale,
)}
</Table.Td>
</Table.Tr>
<Table.Tr>
@@ -4,10 +4,12 @@ import { differenceInDays, isAfter } from "date-fns";
export const GRACE_PERIOD_DAYS = 10;
export function isLicenseExpired(license: ILicenseInfo): boolean {
if (license.lifetime) return false;
return isAfter(new Date(), license.expiresAt);
}
export function daysToExpire(license: ILicenseInfo): number {
if (license.lifetime) return Infinity;
const days = differenceInDays(license.expiresAt, new Date());
return days > 0 ? days : 0;
}
@@ -6,6 +6,7 @@ export interface ILicenseInfo {
seatCount: number;
licenseType: LicenseType;
issuedAt: Date;
expiresAt: Date;
expiresAt: Date | null;
trial: boolean;
lifetime: boolean;
}
@@ -12,19 +12,13 @@ import useUserRole from "@/hooks/use-user-role.tsx";
import { useTranslation } from "react-i18next";
import { IUser } from "@/features/user/types/user.types.ts";
import Paginate from "@/components/common/paginate.tsx";
import { SearchInput } from "@/components/common/search-input.tsx";
import NoTableResults from "@/components/common/no-table-results.tsx";
import { usePaginateAndSearch } from "@/hooks/use-paginate-and-search.tsx";
import { useCursorPaginate } from "@/hooks/use-cursor-paginate";
export default function GroupMembersList() {
const { t } = useTranslation();
const { groupId } = useParams();
const { search, cursor, goNext, goPrev, handleSearch } =
usePaginateAndSearch();
const { data, isLoading } = useGroupMembersQuery(groupId, {
cursor,
query: search,
});
const { cursor, goNext, goPrev } = useCursorPaginate();
const { data, isLoading } = useGroupMembersQuery(groupId, { cursor });
const removeGroupMember = useRemoveGroupMemberMutation();
const { isAdmin } = useUserRole();
@@ -54,7 +48,6 @@ export default function GroupMembersList() {
return (
<>
<SearchInput onSearch={handleSearch} />
<Table.ScrollContainer minWidth={500}>
<Table highlightOnHover verticalSpacing="sm">
<Table.Thead>
@@ -66,62 +59,55 @@ export default function GroupMembersList() {
</Table.Thead>
<Table.Tbody>
{data?.items.length > 0 ? (
data?.items.map((user: IUser, index: number) => (
<Table.Tr key={index}>
<Table.Td>
<Group gap="sm" wrap="nowrap">
<CustomAvatar
avatarUrl={user.avatarUrl}
name={user.name}
/>
<div>
<Text fz="sm" fw={500} lineClamp={1}>
{user.name}
</Text>
<Text fz="xs" c="dimmed">
{user.email}
</Text>
</div>
</Group>
</Table.Td>
<Table.Td>
<Badge variant="light">{t("Active")}</Badge>
</Table.Td>
<Table.Td>
{isAdmin && (
<Menu
shadow="xl"
position="bottom-end"
offset={20}
width={200}
withArrow
arrowPosition="center"
>
<Menu.Target>
<ActionIcon
variant="subtle"
c="gray"
aria-label={t("Member actions for {{name}}", {
name: user.name,
})}
>
<IconDots size={20} stroke={2} />
</ActionIcon>
</Menu.Target>
<Menu.Dropdown>
<Menu.Item onClick={() => openRemoveModal(user.id)}>
{t("Remove group member")}
</Menu.Item>
</Menu.Dropdown>
</Menu>
)}
</Table.Td>
</Table.Tr>
))
) : (
<NoTableResults colSpan={3} />
)}
{data?.items.map((user: IUser, index: number) => (
<Table.Tr key={index}>
<Table.Td>
<Group gap="sm" wrap="nowrap">
<CustomAvatar avatarUrl={user.avatarUrl} name={user.name} />
<div>
<Text fz="sm" fw={500} lineClamp={1}>
{user.name}
</Text>
<Text fz="xs" c="dimmed">
{user.email}
</Text>
</div>
</Group>
</Table.Td>
<Table.Td>
<Badge variant="light">{t("Active")}</Badge>
</Table.Td>
<Table.Td>
{isAdmin && (
<Menu
shadow="xl"
position="bottom-end"
offset={20}
width={200}
withArrow
arrowPosition="center"
>
<Menu.Target>
<ActionIcon
variant="subtle"
c="gray"
aria-label={t("Member actions for {{name}}", {
name: user.name,
})}
>
<IconDots size={20} stroke={2} />
</ActionIcon>
</Menu.Target>
<Menu.Dropdown>
<Menu.Item onClick={() => openRemoveModal(user.id)}>
{t("Remove group member")}
</Menu.Item>
</Menu.Dropdown>
</Menu>
)}
</Table.Td>
</Table.Tr>
))}
</Table.Tbody>
</Table>
</Table.ScrollContainer>
@@ -60,10 +60,6 @@ export class GroupUserRepo {
sql`f_unaccent(users.name)`,
'ilike',
sql`f_unaccent(${'%' + pagination.query + '%'})`,
).or(
sql`users.email`,
'ilike',
sql`f_unaccent(${'%' + pagination.query + '%'})`,
),
);
}