mirror of
https://github.com/fosrl/pangolin.git
synced 2026-05-08 05:17:23 +00:00
Translate the member page
This commit is contained in:
@@ -3208,5 +3208,48 @@
|
||||
"domainPickerWildcardCertWarning": "Wildcard resources may require additional configuration to work properly.",
|
||||
"domainPickerWildcardCertWarningLink": "Learn more",
|
||||
"health": "Health",
|
||||
"domainPendingErrorTitle": "Verification Issue"
|
||||
"domainPendingErrorTitle": "Verification Issue",
|
||||
"memberPortalTitle": "Resources",
|
||||
"memberPortalDescription": "Resources you have access to in this organization",
|
||||
"memberPortalSortBy": "Sort by...",
|
||||
"memberPortalSortNameAsc": "Name A-Z",
|
||||
"memberPortalSortNameDesc": "Name Z-A",
|
||||
"memberPortalSortDomainAsc": "Domain A-Z",
|
||||
"memberPortalSortDomainDesc": "Domain Z-A",
|
||||
"memberPortalSortEnabledFirst": "Enabled First",
|
||||
"memberPortalSortDisabledFirst": "Disabled First",
|
||||
"memberPortalRefresh": "Refresh",
|
||||
"memberPortalRefreshResources": "Refresh Resources",
|
||||
"memberPortalFailedToLoad": "Failed to load resources",
|
||||
"memberPortalFailedToLoadDescription": "Failed to load resources. Please check your connection and try again.",
|
||||
"memberPortalUnableToLoad": "Unable to Load Resources",
|
||||
"memberPortalTryAgain": "Try Again",
|
||||
"memberPortalNoResourcesFound": "No Resources Found",
|
||||
"memberPortalNoResourcesAvailable": "No Resources Available",
|
||||
"memberPortalNoResourcesMatchSearch": "No resources match \"{query}\". Try adjusting your search terms or clearing the search to see all resources.",
|
||||
"memberPortalNoResourcesAccess": "You don't have access to any resources yet. Contact your administrator to get access to resources you need.",
|
||||
"memberPortalClearSearch": "Clear Search",
|
||||
"memberPortalPublicResources": "Public Resources",
|
||||
"memberPortalPublicResourcesDescription": "Web applications and services accessible via browser",
|
||||
"memberPortalCopiedToClipboard": "Copied to clipboard",
|
||||
"memberPortalCopiedUrlDescription": "Resource URL has been copied to your clipboard.",
|
||||
"memberPortalOpenResource": "Open Resource",
|
||||
"memberPortalPrivateResources": "Private Resources",
|
||||
"memberPortalPrivateResourcesDescription": "Internal network resources accessible via client",
|
||||
"memberPortalResourceDetails": "Resource Details",
|
||||
"memberPortalMode": "Mode",
|
||||
"memberPortalDestination": "Destination",
|
||||
"memberPortalAlias": "Alias",
|
||||
"memberPortalCopiedAliasDescription": "Resource alias has been copied to your clipboard.",
|
||||
"memberPortalCopiedDestinationDescription": "Resource destination has been copied to your clipboard.",
|
||||
"memberPortalRequiresClientConnection": "Requires Client Connection",
|
||||
"memberPortalAuthMethods": "Authentication Methods",
|
||||
"memberPortalSso": "Single Sign-On (SSO)",
|
||||
"memberPortalPasswordProtected": "Password Protected",
|
||||
"memberPortalPinCode": "PIN Code",
|
||||
"memberPortalEmailWhitelist": "Email Whitelist",
|
||||
"memberPortalResourceDisabled": "Resource Disabled",
|
||||
"memberPortalShowingResources": "Showing {start}-{end} of {total} resources",
|
||||
"memberPortalPrevious": "Previous",
|
||||
"memberPortalNext": "Next"
|
||||
}
|
||||
|
||||
@@ -123,6 +123,7 @@ const ResourceFavicon = ({
|
||||
|
||||
// Resource Info component
|
||||
const ResourceInfo = ({ resource }: { resource: Resource }) => {
|
||||
const t = useTranslations();
|
||||
const hasAuthMethods =
|
||||
resource.sso ||
|
||||
resource.password ||
|
||||
@@ -141,7 +142,9 @@ const ResourceInfo = ({ resource }: { resource: Resource }) => {
|
||||
{/* Site Information */}
|
||||
{resource.siteName && (
|
||||
<div>
|
||||
<div className="text-xs font-medium mb-1.5">Site</div>
|
||||
<div className="text-xs font-medium mb-1.5">
|
||||
{t("site")}
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Combine className="h-4 w-4 text-foreground shrink-0" />
|
||||
<span className="text-sm">{resource.siteName}</span>
|
||||
@@ -157,7 +160,7 @@ const ResourceInfo = ({ resource }: { resource: Resource }) => {
|
||||
}
|
||||
>
|
||||
<div className="text-xs font-medium mb-1.5">
|
||||
Authentication Methods
|
||||
{t("memberPortalAuthMethods")}
|
||||
</div>
|
||||
<div className="flex flex-col gap-1.5">
|
||||
{resource.sso && (
|
||||
@@ -166,7 +169,7 @@ const ResourceInfo = ({ resource }: { resource: Resource }) => {
|
||||
<Key className="h-3 w-3 text-blue-700 dark:text-blue-300" />
|
||||
</div>
|
||||
<span className="text-sm">
|
||||
Single Sign-On (SSO)
|
||||
{t("memberPortalSso")}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
@@ -176,7 +179,7 @@ const ResourceInfo = ({ resource }: { resource: Resource }) => {
|
||||
<KeyRound className="h-3 w-3 text-purple-700 dark:text-purple-300" />
|
||||
</div>
|
||||
<span className="text-sm">
|
||||
Password Protected
|
||||
{t("memberPortalPasswordProtected")}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
@@ -185,7 +188,9 @@ const ResourceInfo = ({ resource }: { resource: Resource }) => {
|
||||
<div className="h-5 w-5 rounded-full flex items-center justify-center bg-emerald-50/50 dark:bg-emerald-950/50">
|
||||
<Fingerprint className="h-3 w-3 text-emerald-700 dark:text-emerald-300" />
|
||||
</div>
|
||||
<span className="text-sm">PIN Code</span>
|
||||
<span className="text-sm">
|
||||
{t("memberPortalPinCode")}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{resource.whitelist && (
|
||||
@@ -193,7 +198,9 @@ const ResourceInfo = ({ resource }: { resource: Resource }) => {
|
||||
<div className="h-5 w-5 rounded-full flex items-center justify-center bg-amber-50/50 dark:bg-amber-950/50">
|
||||
<AtSign className="h-3 w-3 text-amber-700 dark:text-amber-300" />
|
||||
</div>
|
||||
<span className="text-sm">Email Whitelist</span>
|
||||
<span className="text-sm">
|
||||
{t("memberPortalEmailWhitelist")}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -208,7 +215,7 @@ const ResourceInfo = ({ resource }: { resource: Resource }) => {
|
||||
<div className="flex items-center gap-2">
|
||||
<AlertCircle className="h-4 w-4 text-destructive shrink-0" />
|
||||
<span className="text-sm text-destructive">
|
||||
Resource Disabled
|
||||
{t("memberPortalResourceDisabled")}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -233,6 +240,7 @@ const PaginationControls = ({
|
||||
totalItems: number;
|
||||
itemsPerPage: number;
|
||||
}) => {
|
||||
const t = useTranslations();
|
||||
const startItem = (currentPage - 1) * itemsPerPage + 1;
|
||||
const endItem = Math.min(currentPage * itemsPerPage, totalItems);
|
||||
|
||||
@@ -241,7 +249,11 @@ const PaginationControls = ({
|
||||
return (
|
||||
<div className="flex flex-col sm:flex-row items-center justify-between gap-4 mt-8">
|
||||
<div className="text-sm text-muted-foreground">
|
||||
Showing {startItem}-{endItem} of {totalItems} resources
|
||||
{t("memberPortalShowingResources", {
|
||||
start: startItem,
|
||||
end: endItem,
|
||||
total: totalItems
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
@@ -253,7 +265,7 @@ const PaginationControls = ({
|
||||
className="gap-1"
|
||||
>
|
||||
<ChevronLeft className="h-4 w-4" />
|
||||
Previous
|
||||
{t("memberPortalPrevious")}
|
||||
</Button>
|
||||
|
||||
<div className="flex items-center gap-1">
|
||||
@@ -309,7 +321,7 @@ const PaginationControls = ({
|
||||
disabled={currentPage === totalPages}
|
||||
className="gap-1"
|
||||
>
|
||||
Next
|
||||
{t("memberPortalNext")}
|
||||
<ChevronRight className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
@@ -389,13 +401,11 @@ export default function MemberResourcesPortal({
|
||||
response.data.data.siteResources || []
|
||||
);
|
||||
} else {
|
||||
setError("Failed to load resources");
|
||||
setError(t("memberPortalFailedToLoad"));
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Error fetching user resources:", err);
|
||||
setError(
|
||||
"Failed to load resources. Please check your connection and try again."
|
||||
);
|
||||
setError(t("memberPortalFailedToLoadDescription"));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
setRefreshing(false);
|
||||
@@ -526,8 +536,8 @@ export default function MemberResourcesPortal({
|
||||
return (
|
||||
<div className="container mx-auto max-w-12xl">
|
||||
<SettingsSectionTitle
|
||||
title="Resources"
|
||||
description="Resources you have access to in this organization"
|
||||
title={t("memberPortalTitle")}
|
||||
description={t("memberPortalDescription")}
|
||||
/>
|
||||
|
||||
{/* Search and Sort Controls - Skeleton */}
|
||||
@@ -554,8 +564,8 @@ export default function MemberResourcesPortal({
|
||||
return (
|
||||
<div className="container mx-auto max-w-12xl">
|
||||
<SettingsSectionTitle
|
||||
title="Resources"
|
||||
description="Resources you have access to in this organization"
|
||||
title={t("memberPortalTitle")}
|
||||
description={t("memberPortalDescription")}
|
||||
/>
|
||||
<Card>
|
||||
<CardContent className="flex flex-col items-center justify-center py-20 text-center">
|
||||
@@ -563,7 +573,7 @@ export default function MemberResourcesPortal({
|
||||
<AlertCircle className="h-16 w-16 text-destructive/60" />
|
||||
</div>
|
||||
<h3 className="text-xl font-semibold text-foreground mb-3">
|
||||
Unable to Load Resources
|
||||
{t("memberPortalUnableToLoad")}
|
||||
</h3>
|
||||
<p className="text-muted-foreground max-w-lg text-base mb-6">
|
||||
{error}
|
||||
@@ -574,7 +584,7 @@ export default function MemberResourcesPortal({
|
||||
className="gap-2"
|
||||
>
|
||||
<RefreshCw className="h-4 w-4" />
|
||||
Try Again
|
||||
{t("memberPortalTryAgain")}
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@@ -585,8 +595,8 @@ export default function MemberResourcesPortal({
|
||||
return (
|
||||
<div className="container mx-auto max-w-12xl">
|
||||
<SettingsSectionTitle
|
||||
title="Resources"
|
||||
description="Resources you have access to in this organization"
|
||||
title={t("memberPortalTitle")}
|
||||
description={t("memberPortalDescription")}
|
||||
/>
|
||||
|
||||
{/* Search and Sort Controls with Refresh */}
|
||||
@@ -595,7 +605,7 @@ export default function MemberResourcesPortal({
|
||||
{/* Search */}
|
||||
<div className="relative w-full sm:w-80">
|
||||
<Input
|
||||
placeholder="Search resources..."
|
||||
placeholder={t("resourcesSearch")}
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
className="w-full pl-8 bg-card"
|
||||
@@ -607,26 +617,28 @@ export default function MemberResourcesPortal({
|
||||
<div className="w-full sm:w-36">
|
||||
<Select value={sortBy} onValueChange={setSortBy}>
|
||||
<SelectTrigger className="bg-card">
|
||||
<SelectValue placeholder="Sort by..." />
|
||||
<SelectValue
|
||||
placeholder={t("memberPortalSortBy")}
|
||||
/>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="name-asc">
|
||||
Name A-Z
|
||||
{t("memberPortalSortNameAsc")}
|
||||
</SelectItem>
|
||||
<SelectItem value="name-desc">
|
||||
Name Z-A
|
||||
{t("memberPortalSortNameDesc")}
|
||||
</SelectItem>
|
||||
<SelectItem value="domain-asc">
|
||||
Domain A-Z
|
||||
{t("memberPortalSortDomainAsc")}
|
||||
</SelectItem>
|
||||
<SelectItem value="domain-desc">
|
||||
Domain Z-A
|
||||
{t("memberPortalSortDomainDesc")}
|
||||
</SelectItem>
|
||||
<SelectItem value="status-enabled">
|
||||
Enabled First
|
||||
{t("memberPortalSortEnabledFirst")}
|
||||
</SelectItem>
|
||||
<SelectItem value="status-disabled">
|
||||
Disabled First
|
||||
{t("memberPortalSortDisabledFirst")}
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
@@ -644,7 +656,7 @@ export default function MemberResourcesPortal({
|
||||
<RefreshCw
|
||||
className={`h-4 w-4 ${refreshing ? "animate-spin" : ""}`}
|
||||
/>
|
||||
Refresh
|
||||
{t("memberPortalRefresh")}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -663,13 +675,15 @@ export default function MemberResourcesPortal({
|
||||
</div>
|
||||
<h3 className="text-2xl font-semibold text-foreground mb-3">
|
||||
{searchQuery
|
||||
? "No Resources Found"
|
||||
: "No Resources Available"}
|
||||
? t("memberPortalNoResourcesFound")
|
||||
: t("memberPortalNoResourcesAvailable")}
|
||||
</h3>
|
||||
<p className="text-muted-foreground max-w-lg text-base mb-6">
|
||||
{searchQuery
|
||||
? `No resources match "${searchQuery}". Try adjusting your search terms or clearing the search to see all resources.`
|
||||
: "You don't have access to any resources yet. Contact your administrator to get access to resources you need."}
|
||||
? t("memberPortalNoResourcesMatchSearch", {
|
||||
query: searchQuery
|
||||
})
|
||||
: t("memberPortalNoResourcesAccess")}
|
||||
</p>
|
||||
<div className="flex flex-col sm:flex-row gap-3">
|
||||
{searchQuery ? (
|
||||
@@ -678,7 +692,7 @@ export default function MemberResourcesPortal({
|
||||
variant="outline"
|
||||
className="gap-2"
|
||||
>
|
||||
Clear Search
|
||||
{t("memberPortalClearSearch")}
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
@@ -690,7 +704,7 @@ export default function MemberResourcesPortal({
|
||||
<RefreshCw
|
||||
className={`h-4 w-4 ${refreshing ? "animate-spin" : ""}`}
|
||||
/>
|
||||
Refresh Resources
|
||||
{t("memberPortalRefreshResources")}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
@@ -704,11 +718,12 @@ export default function MemberResourcesPortal({
|
||||
<div className="mb-4">
|
||||
<h3 className="text-lg font-semibold text-foreground flex items-center gap-2">
|
||||
<Globe className="h-5 w-5" />
|
||||
Public Resources
|
||||
{t("memberPortalPublicResources")}
|
||||
</h3>
|
||||
<p className="text-sm text-muted-foreground mt-1">
|
||||
Web applications and services accessible via
|
||||
browser
|
||||
{t(
|
||||
"memberPortalPublicResourcesDescription"
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid gap-5 grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-3 2xl:grid-cols-4 auto-cols-fr mb-8">
|
||||
@@ -768,9 +783,12 @@ export default function MemberResourcesPortal({
|
||||
resource.domain
|
||||
);
|
||||
toast({
|
||||
title: "Copied to clipboard",
|
||||
description:
|
||||
"Resource URL has been copied to your clipboard.",
|
||||
title: t(
|
||||
"memberPortalCopiedToClipboard"
|
||||
),
|
||||
description: t(
|
||||
"memberPortalCopiedUrlDescription"
|
||||
),
|
||||
duration: 2000
|
||||
});
|
||||
}}
|
||||
@@ -791,7 +809,7 @@ export default function MemberResourcesPortal({
|
||||
disabled={!resource.enabled}
|
||||
>
|
||||
<ExternalLink className="h-3.5 w-3.5 mr-2" />
|
||||
Open Resource
|
||||
{t("memberPortalOpenResource")}
|
||||
</Button>
|
||||
</div>
|
||||
</Card>
|
||||
@@ -806,11 +824,12 @@ export default function MemberResourcesPortal({
|
||||
<div className="mb-4">
|
||||
<h3 className="text-lg font-semibold text-foreground flex items-center gap-2">
|
||||
<Combine className="h-5 w-5" />
|
||||
Private Resources
|
||||
{t("memberPortalPrivateResources")}
|
||||
</h3>
|
||||
<p className="text-sm text-muted-foreground mt-1">
|
||||
Internal network resources accessible via
|
||||
client
|
||||
{t(
|
||||
"memberPortalPrivateResourcesDescription"
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid gap-5 grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-3 2xl:grid-cols-4 auto-cols-fr mb-8">
|
||||
@@ -843,11 +862,16 @@ export default function MemberResourcesPortal({
|
||||
<InfoPopup>
|
||||
<div className="space-y-2 text-sm">
|
||||
<div className="text-xs font-medium mb-1.5">
|
||||
Resource Details
|
||||
{t(
|
||||
"memberPortalResourceDetails"
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<span className="font-medium">
|
||||
Mode:
|
||||
{t(
|
||||
"memberPortalMode"
|
||||
)}
|
||||
:
|
||||
</span>
|
||||
<span className="ml-2 text-muted-foreground capitalize">
|
||||
{
|
||||
@@ -858,7 +882,10 @@ export default function MemberResourcesPortal({
|
||||
{siteResource.protocol && (
|
||||
<div>
|
||||
<span className="font-medium">
|
||||
Protocol:
|
||||
{t(
|
||||
"protocol"
|
||||
)}
|
||||
:
|
||||
</span>
|
||||
<span className="ml-2 text-muted-foreground uppercase">
|
||||
{
|
||||
@@ -869,7 +896,10 @@ export default function MemberResourcesPortal({
|
||||
)}
|
||||
<div>
|
||||
<span className="font-medium">
|
||||
Destination:
|
||||
{t(
|
||||
"memberPortalDestination"
|
||||
)}
|
||||
:
|
||||
</span>
|
||||
<span className="ml-2 text-muted-foreground">
|
||||
{
|
||||
@@ -880,7 +910,10 @@ export default function MemberResourcesPortal({
|
||||
{siteResource.alias && (
|
||||
<div>
|
||||
<span className="font-medium">
|
||||
Alias:
|
||||
{t(
|
||||
"memberPortalAlias"
|
||||
)}
|
||||
:
|
||||
</span>
|
||||
<span className="ml-2 text-muted-foreground">
|
||||
{
|
||||
@@ -891,14 +924,21 @@ export default function MemberResourcesPortal({
|
||||
)}
|
||||
<div>
|
||||
<span className="font-medium">
|
||||
Status:
|
||||
{t(
|
||||
"status"
|
||||
)}
|
||||
:
|
||||
</span>
|
||||
<span
|
||||
className={`ml-2 ${siteResource.enabled ? "text-green-600" : "text-red-600"}`}
|
||||
>
|
||||
{siteResource.enabled
|
||||
? "Enabled"
|
||||
: "Disabled"}
|
||||
? t(
|
||||
"enabled"
|
||||
)
|
||||
: t(
|
||||
"disabled"
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -925,9 +965,13 @@ export default function MemberResourcesPortal({
|
||||
siteResource.alias!
|
||||
);
|
||||
toast({
|
||||
title: "Copied to clipboard",
|
||||
title: t(
|
||||
"memberPortalCopiedToClipboard"
|
||||
),
|
||||
description:
|
||||
"Resource alias has been copied to your clipboard.",
|
||||
t(
|
||||
"memberPortalCopiedAliasDescription"
|
||||
),
|
||||
duration: 2000
|
||||
});
|
||||
}}
|
||||
@@ -959,9 +1003,13 @@ export default function MemberResourcesPortal({
|
||||
siteResource.destination
|
||||
);
|
||||
toast({
|
||||
title: "Copied to clipboard",
|
||||
title: t(
|
||||
"memberPortalCopiedToClipboard"
|
||||
),
|
||||
description:
|
||||
"Resource destination has been copied to your clipboard.",
|
||||
t(
|
||||
"memberPortalCopiedDestinationDescription"
|
||||
),
|
||||
duration: 2000
|
||||
});
|
||||
}}
|
||||
@@ -976,7 +1024,9 @@ export default function MemberResourcesPortal({
|
||||
<div className="p-6 pt-0 mt-auto">
|
||||
<div className="flex items-center justify-center py-2 px-4 bg-muted/50 rounded text-sm text-muted-foreground">
|
||||
<Combine className="h-3.5 w-3.5 mr-2" />
|
||||
Requires Client Connection
|
||||
{t(
|
||||
"memberPortalRequiresClientConnection"
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
Reference in New Issue
Block a user