mirror of
https://github.com/langgenius/dify.git
synced 2026-05-10 05:56:31 +08:00
style
This commit is contained in:
parent
e16988d8a9
commit
bdd73d2846
@ -139,7 +139,7 @@ function CreateInstanceForm() {
|
||||
type="text"
|
||||
placeholder={sourceApp?.name ?? t('createModal.namePlaceholder')}
|
||||
required
|
||||
className="flex h-8 items-center rounded-lg border-[0.5px] border-components-input-border-active bg-components-input-bg-normal px-3 text-[13px] font-medium text-text-secondary outline-hidden placeholder:text-text-quaternary"
|
||||
className="flex h-8 items-center rounded-lg border border-components-input-border-active bg-components-input-bg-normal px-3 system-sm-medium text-text-secondary outline-hidden placeholder:text-text-quaternary"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -151,7 +151,7 @@ function CreateInstanceForm() {
|
||||
id="instance-desc"
|
||||
name="description"
|
||||
placeholder={t('createModal.descriptionPlaceholder')}
|
||||
className="min-h-[80px] rounded-lg border-[0.5px] border-components-input-border-active bg-components-input-bg-normal px-3 py-2 text-[13px] text-text-secondary outline-hidden placeholder:text-text-quaternary"
|
||||
className="min-h-20 rounded-lg border border-components-input-border-active bg-components-input-bg-normal px-3 py-2 system-sm-regular text-text-secondary outline-hidden placeholder:text-text-quaternary"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -176,7 +176,7 @@ export function CreateInstanceModal() {
|
||||
open={open}
|
||||
onOpenChange={next => !next && closeModal()}
|
||||
>
|
||||
<DialogContent className="w-[520px] max-w-[90vw]">
|
||||
<DialogContent className="w-130 max-w-[90vw]">
|
||||
<DialogCloseButton />
|
||||
{open && <CreateInstanceForm />}
|
||||
</DialogContent>
|
||||
|
||||
@ -49,14 +49,14 @@ export function DeployDrawer() {
|
||||
open={open}
|
||||
onOpenChange={next => !next && closeDeployDrawer()}
|
||||
>
|
||||
<DialogContent className="w-[560px] max-w-[90vw]">
|
||||
<DialogContent className="w-140 max-w-[90vw]">
|
||||
<DialogCloseButton />
|
||||
{!drawerAppInstanceId
|
||||
? <div className="p-4 text-text-tertiary">{t('deployDrawer.notFound')}</div>
|
||||
: (!releaseHistory || !environmentOptionsReply)
|
||||
? (
|
||||
<div className="flex items-center gap-2 p-4 system-sm-regular text-text-tertiary">
|
||||
<span className="h-4 w-4 animate-spin rounded-full border-2 border-components-panel-border border-t-transparent" />
|
||||
<span className="size-4 animate-spin rounded-full border-2 border-components-panel-border border-t-transparent" />
|
||||
{t('createModal.loadingApps')}
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -53,7 +53,7 @@ export function DeploymentSelect({ value, onChange, options, placeholder }: Sele
|
||||
>
|
||||
<SelectTrigger
|
||||
className={cn(
|
||||
'h-8 min-w-0 border-[0.5px] border-components-input-border-active px-2 text-left system-sm-medium',
|
||||
'h-8 min-w-0 border border-components-input-border-active px-2 text-left system-sm-medium',
|
||||
!selectedOption && 'text-text-quaternary',
|
||||
)}
|
||||
>
|
||||
|
||||
@ -25,7 +25,7 @@ export function StatusBadge({ status, className }: {
|
||||
return (
|
||||
<span className={cn(baseBadge, statusStyles[status], className)}>
|
||||
{status === 'deploying' && (
|
||||
<span className="h-1.5 w-1.5 animate-pulse rounded-full bg-current" />
|
||||
<span className="size-1.5 animate-pulse rounded-full bg-current" />
|
||||
)}
|
||||
{t(statusKey[status])}
|
||||
</span>
|
||||
|
||||
@ -8,7 +8,7 @@ export function AccessTab({ appInstanceId }: {
|
||||
appInstanceId: string
|
||||
}) {
|
||||
return (
|
||||
<div className="flex w-full max-w-[960px] flex-col gap-5 p-6">
|
||||
<div className="flex w-full max-w-240 flex-col gap-5 p-6">
|
||||
<AccessPermissionsSection appInstanceId={appInstanceId} />
|
||||
<AccessChannelsSection appInstanceId={appInstanceId} />
|
||||
<DeveloperApiSection appInstanceId={appInstanceId} />
|
||||
|
||||
@ -39,23 +39,23 @@ function ApiKeyRow({ appInstanceId, apiKey }: {
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-3 py-1.5">
|
||||
<div className="flex min-w-[140px] flex-col">
|
||||
<div className="flex min-w-35 flex-col">
|
||||
<span className="system-sm-medium text-text-primary">{apiKey.name || apiKey.id}</span>
|
||||
<span className="system-xs-regular text-text-tertiary">
|
||||
{t('access.api.envPrefix', { env: environmentLabel })}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex min-w-0 flex-1 items-center gap-1 rounded-lg border-[0.5px] border-components-input-border-active bg-components-input-bg-normal pr-1 pl-2">
|
||||
<div className="min-w-0 flex-1 truncate font-mono text-[13px] font-medium text-text-secondary">
|
||||
<div className="flex min-w-0 flex-1 items-center gap-1 rounded-lg border border-components-input-border-active bg-components-input-bg-normal pr-1 pl-2">
|
||||
<div className="min-w-0 flex-1 truncate font-mono system-sm-medium text-text-secondary">
|
||||
{displayValue}
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleRevoke}
|
||||
aria-label={t('access.revoke')}
|
||||
className="flex h-6 w-6 shrink-0 items-center justify-center rounded-md text-text-tertiary hover:bg-state-destructive-hover hover:text-text-destructive"
|
||||
className="flex size-6 shrink-0 items-center justify-center rounded-md text-text-tertiary hover:bg-state-destructive-hover hover:text-text-destructive"
|
||||
>
|
||||
<span className="i-ri-delete-bin-line h-3.5 w-3.5" />
|
||||
<span className="i-ri-delete-bin-line size-3.5" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -131,12 +131,12 @@ export function ApiKeyGenerateMenu({ appInstanceId, environments, apiKeys }: {
|
||||
disabled && 'cursor-not-allowed opacity-50',
|
||||
)}
|
||||
>
|
||||
<span className="i-ri-add-line h-3.5 w-3.5" />
|
||||
<span className="i-ri-add-line size-3.5" />
|
||||
{t('access.api.newKey')}
|
||||
<span className="i-ri-arrow-down-s-line h-3.5 w-3.5" />
|
||||
<span className="i-ri-arrow-down-s-line size-3.5" />
|
||||
</DropdownMenuTrigger>
|
||||
{open && !disabled && (
|
||||
<DropdownMenuContent placement="bottom-end" sideOffset={4} popupClassName="w-[220px]">
|
||||
<DropdownMenuContent placement="bottom-end" sideOffset={4} popupClassName="w-55">
|
||||
{selectableEnvironments.map(env => (
|
||||
<DropdownMenuItem
|
||||
key={env.id}
|
||||
|
||||
@ -115,7 +115,7 @@ export function AccessChannelsSection({
|
||||
<CopyPill
|
||||
label={t('access.cli.domain')}
|
||||
value={cliDomain}
|
||||
className="min-w-[260px] flex-1"
|
||||
className="min-w-65 flex-1"
|
||||
/>
|
||||
<a
|
||||
href={cliDocsUrl}
|
||||
@ -123,7 +123,7 @@ export function AccessChannelsSection({
|
||||
rel="noreferrer"
|
||||
className="inline-flex h-8 shrink-0 items-center gap-1.5 rounded-lg border border-components-button-secondary-border bg-components-button-secondary-bg px-3 system-sm-medium text-components-button-secondary-text hover:bg-components-button-secondary-bg-hover"
|
||||
>
|
||||
<span className="i-ri-download-cloud-2-line h-3.5 w-3.5" />
|
||||
<span className="i-ri-download-cloud-2-line size-3.5" />
|
||||
{t('access.cli.install')}
|
||||
</a>
|
||||
<a
|
||||
@ -132,7 +132,7 @@ export function AccessChannelsSection({
|
||||
rel="noreferrer"
|
||||
className="inline-flex h-8 shrink-0 items-center gap-1.5 rounded-lg border border-components-button-secondary-border bg-components-button-secondary-bg px-3 system-sm-medium text-components-button-secondary-text hover:bg-components-button-secondary-bg-hover"
|
||||
>
|
||||
<span className="i-ri-book-open-line h-3.5 w-3.5" />
|
||||
<span className="i-ri-book-open-line size-3.5" />
|
||||
{t('access.cli.docs')}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@ -24,25 +24,25 @@ export function CopyPill({ label, value, prefix, className }: CopyPillProps) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'flex h-8 items-center rounded-lg border-[0.5px] border-components-input-border-active bg-components-input-bg-normal pr-1 pl-1.5',
|
||||
'flex h-8 items-center gap-1 rounded-lg border border-components-input-border-active bg-components-input-bg-normal pr-1 pl-1.5',
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<div className="mr-0.5 flex h-5 shrink-0 items-center rounded-md border border-divider-subtle px-1.5 text-[11px] font-medium text-text-tertiary">
|
||||
<div className="flex h-5 shrink-0 items-center rounded-md border border-divider-subtle px-1.5 system-2xs-medium text-text-tertiary">
|
||||
{label}
|
||||
</div>
|
||||
{prefix}
|
||||
<div className="min-w-0 flex-1 truncate px-1 font-mono text-[13px] font-medium text-text-secondary">
|
||||
<div className="min-w-0 flex-1 truncate px-1 font-mono system-sm-medium text-text-secondary">
|
||||
{value}
|
||||
</div>
|
||||
<div className="mx-1 h-[14px] w-px shrink-0 bg-divider-regular" />
|
||||
<div className="h-3.5 w-px shrink-0 bg-divider-regular" />
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => copy(value)}
|
||||
aria-label={t('access.copy')}
|
||||
className="flex h-6 w-6 shrink-0 items-center justify-center rounded-md text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary"
|
||||
className="flex size-6 shrink-0 items-center justify-center rounded-md text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary"
|
||||
>
|
||||
<span className={cn(copied ? 'i-ri-check-line' : 'i-ri-file-copy-line', 'h-3.5 w-3.5')} />
|
||||
<span className={cn(copied ? 'i-ri-check-line' : 'i-ri-file-copy-line', 'size-3.5')} />
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
@ -58,10 +58,10 @@ type EndpointRowProps = {
|
||||
export function EndpointRow({ envName, label, value, openLabel }: EndpointRowProps) {
|
||||
return (
|
||||
<div className="flex flex-wrap items-center gap-x-3 gap-y-1.5">
|
||||
<span className="min-w-[140px] system-xs-regular text-text-tertiary">
|
||||
<span className="min-w-35 system-xs-regular text-text-tertiary">
|
||||
{envName}
|
||||
</span>
|
||||
<CopyPill label={label} value={value} className="min-w-[260px] flex-1" />
|
||||
<CopyPill label={label} value={value} className="min-w-65 flex-1" />
|
||||
{openLabel && (
|
||||
<a
|
||||
href={value}
|
||||
@ -69,7 +69,7 @@ export function EndpointRow({ envName, label, value, openLabel }: EndpointRowPro
|
||||
rel="noreferrer"
|
||||
className="inline-flex h-8 shrink-0 items-center gap-1.5 rounded-lg border border-components-button-secondary-border bg-components-button-secondary-bg px-3 system-sm-medium text-components-button-secondary-text hover:bg-components-button-secondary-bg-hover"
|
||||
>
|
||||
<span className="i-ri-external-link-line h-3.5 w-3.5" />
|
||||
<span className="i-ri-external-link-line size-3.5" />
|
||||
{openLabel}
|
||||
</a>
|
||||
)}
|
||||
|
||||
@ -69,9 +69,9 @@ function CreatedApiTokenCard({ appInstanceId }: {
|
||||
type="button"
|
||||
onClick={() => setCreatedApiToken(undefined)}
|
||||
aria-label={t('access.api.dismissToken')}
|
||||
className="flex h-6 w-6 shrink-0 items-center justify-center rounded-md text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary"
|
||||
className="flex size-6 shrink-0 items-center justify-center rounded-md text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary"
|
||||
>
|
||||
<span className="i-ri-close-line h-3.5 w-3.5" />
|
||||
<span className="i-ri-close-line size-3.5" />
|
||||
</button>
|
||||
</div>
|
||||
<CopyPill
|
||||
|
||||
@ -50,15 +50,15 @@ function PermissionPicker({ value, disabled, onChange }: {
|
||||
<DropdownMenuTrigger
|
||||
disabled={disabled}
|
||||
className={cn(
|
||||
'inline-flex h-8 min-w-[220px] items-center gap-2 rounded-lg border-[0.5px] border-components-input-border-active bg-components-input-bg-normal px-2.5 system-sm-regular text-text-secondary hover:bg-state-base-hover',
|
||||
'inline-flex h-8 min-w-55 items-center gap-2 rounded-lg border border-components-input-border-active bg-components-input-bg-normal px-2.5 system-sm-regular text-text-secondary hover:bg-state-base-hover',
|
||||
disabled && 'opacity-50',
|
||||
)}
|
||||
>
|
||||
<span className={cn(icon, 'h-4 w-4 shrink-0 text-text-tertiary')} />
|
||||
<span className={cn(icon, 'size-4 shrink-0 text-text-tertiary')} />
|
||||
<span className="flex-1 truncate text-left">{label}</span>
|
||||
<span className="i-ri-arrow-down-s-line h-4 w-4 shrink-0 text-text-tertiary" />
|
||||
<span className="i-ri-arrow-down-s-line size-4 shrink-0 text-text-tertiary" />
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent placement="bottom-start" popupClassName="w-[340px] p-1">
|
||||
<DropdownMenuContent placement="bottom-start" popupClassName="w-85 p-1">
|
||||
{permissionOrder.map((kind) => {
|
||||
const itemIcon = permissionIcon[kind]
|
||||
const isSelected = kind === value
|
||||
@ -68,7 +68,7 @@ function PermissionPicker({ value, disabled, onChange }: {
|
||||
onSelect={() => onChange(kind)}
|
||||
className="mx-0 h-auto items-start gap-3 rounded-lg px-2.5 py-2"
|
||||
>
|
||||
<span className={cn(itemIcon, 'mt-0.5 h-4 w-4 shrink-0 text-text-tertiary')} />
|
||||
<span className={cn(itemIcon, 'mt-0.5 size-4 shrink-0 text-text-tertiary')} />
|
||||
<div className="flex min-w-0 flex-1 flex-col">
|
||||
<div className="flex min-w-0 items-center gap-2">
|
||||
<span className="truncate system-sm-medium text-text-primary">
|
||||
@ -80,7 +80,7 @@ function PermissionPicker({ value, disabled, onChange }: {
|
||||
</span>
|
||||
</div>
|
||||
{isSelected && (
|
||||
<span className="mt-0.5 i-ri-check-line h-4 w-4 shrink-0 text-text-accent" />
|
||||
<span className="mt-0.5 i-ri-check-line size-4 shrink-0 text-text-accent" />
|
||||
)}
|
||||
</DropdownMenuItem>
|
||||
)
|
||||
@ -149,7 +149,7 @@ function SubjectPill({ subject, disabled, onRemove }: {
|
||||
|
||||
return (
|
||||
<div className="inline-flex max-w-full items-center gap-1 rounded-full border border-divider-subtle bg-components-badge-white-to-dark px-2 py-1">
|
||||
<span className={cn(isGroup ? 'i-ri-group-line' : 'i-ri-user-line', 'h-3.5 w-3.5 shrink-0 text-text-tertiary')} />
|
||||
<span className={cn(isGroup ? 'i-ri-group-line' : 'i-ri-user-line', 'size-3.5 shrink-0 text-text-tertiary')} />
|
||||
<span className="truncate system-xs-medium text-text-secondary">{subject.name || subject.id}</span>
|
||||
{isGroup && subject.memberCount != null && (
|
||||
<span className="system-2xs-regular text-text-tertiary">{subject.memberCount}</span>
|
||||
@ -160,11 +160,11 @@ function SubjectPill({ subject, disabled, onRemove }: {
|
||||
onClick={onRemove}
|
||||
aria-label={t('operation.remove', { ns: 'common' })}
|
||||
className={cn(
|
||||
'flex h-4 w-4 shrink-0 items-center justify-center rounded-full text-text-quaternary hover:text-text-secondary',
|
||||
'flex size-4 shrink-0 items-center justify-center rounded-full text-text-quaternary hover:text-text-secondary',
|
||||
disabled && 'cursor-not-allowed opacity-40',
|
||||
)}
|
||||
>
|
||||
<span className="i-ri-close-circle-fill h-3.5 w-3.5" />
|
||||
<span className="i-ri-close-circle-fill size-3.5" />
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
@ -226,22 +226,22 @@ function SubjectPicker({
|
||||
disabled && 'cursor-not-allowed opacity-50',
|
||||
)}
|
||||
>
|
||||
<span className="i-ri-add-line h-3.5 w-3.5" />
|
||||
<span className="i-ri-add-line size-3.5" />
|
||||
{t('access.members.pickPlaceholder')}
|
||||
</button>
|
||||
)}
|
||||
/>
|
||||
{open && (
|
||||
<PopoverContent placement="bottom-start" sideOffset={4} popupClassName="w-[360px] p-0">
|
||||
<div className="flex max-h-[420px] flex-col overflow-hidden rounded-xl border border-components-panel-border bg-components-panel-bg shadow-lg">
|
||||
<PopoverContent placement="bottom-start" sideOffset={4} popupClassName="w-90 p-0">
|
||||
<div className="flex max-h-105 flex-col overflow-hidden rounded-xl border border-components-panel-border bg-components-panel-bg shadow-lg">
|
||||
<div className="border-b border-divider-subtle p-2">
|
||||
<div className="flex h-8 items-center gap-2 rounded-lg border-[0.5px] border-components-input-border-active bg-components-input-bg-normal px-2">
|
||||
<span className="i-ri-search-line h-4 w-4 shrink-0 text-text-tertiary" />
|
||||
<div className="flex h-8 items-center gap-2 rounded-lg border border-components-input-border-active bg-components-input-bg-normal px-2">
|
||||
<span className="i-ri-search-line size-4 shrink-0 text-text-tertiary" />
|
||||
<input
|
||||
value={keyword}
|
||||
onChange={e => setKeyword(e.target.value)}
|
||||
placeholder={t('access.members.searchPlaceholder')}
|
||||
className="min-w-0 flex-1 bg-transparent system-sm-regular text-text-primary outline-none placeholder:text-text-quaternary"
|
||||
className="min-w-0 flex-1 bg-transparent system-sm-regular text-text-primary outline-hidden placeholder:text-text-quaternary"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -249,7 +249,7 @@ function SubjectPicker({
|
||||
{subjectsQuery.isLoading
|
||||
? (
|
||||
<div className="flex h-16 items-center justify-center">
|
||||
<span className="h-4 w-4 animate-spin rounded-full border-2 border-components-panel-border border-t-transparent" />
|
||||
<span className="size-4 animate-spin rounded-full border-2 border-components-panel-border border-t-transparent" />
|
||||
</div>
|
||||
)
|
||||
: subjects.length === 0
|
||||
@ -268,7 +268,7 @@ function SubjectPicker({
|
||||
onClick={() => toggleSubject(subject)}
|
||||
className="flex w-full items-center gap-2 rounded-lg px-2 py-2 text-left hover:bg-state-base-hover"
|
||||
>
|
||||
<span className={cn(isGroup ? 'i-ri-group-line' : 'i-ri-user-line', 'h-4 w-4 shrink-0 text-text-tertiary')} />
|
||||
<span className={cn(isGroup ? 'i-ri-group-line' : 'i-ri-user-line', 'size-4 shrink-0 text-text-tertiary')} />
|
||||
<span className="min-w-0 flex-1 truncate system-sm-medium text-text-secondary">
|
||||
{subject.name || subject.id}
|
||||
</span>
|
||||
@ -278,7 +278,7 @@ function SubjectPicker({
|
||||
</span>
|
||||
)}
|
||||
{isSelected && (
|
||||
<span className="i-ri-check-line h-4 w-4 shrink-0 text-text-accent" />
|
||||
<span className="i-ri-check-line size-4 shrink-0 text-text-accent" />
|
||||
)}
|
||||
</button>
|
||||
)
|
||||
@ -390,7 +390,7 @@ export function EnvironmentPermissionRow({
|
||||
return (
|
||||
<div className="flex flex-col gap-1.5">
|
||||
<div className="flex flex-wrap items-center gap-x-3 gap-y-1.5">
|
||||
<span className="min-w-[140px] system-xs-regular text-text-tertiary">
|
||||
<span className="min-w-35 system-xs-regular text-text-tertiary">
|
||||
{environmentName(environment)}
|
||||
</span>
|
||||
<PermissionPicker
|
||||
@ -400,7 +400,7 @@ export function EnvironmentPermissionRow({
|
||||
/>
|
||||
</div>
|
||||
{permissionKind === 'specific' && (
|
||||
<div className="flex flex-col gap-2 pl-0 sm:pl-[152px]">
|
||||
<div className="flex flex-col gap-2 pl-0 sm:pl-38">
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<SubjectPicker
|
||||
appInstanceId={appInstanceId}
|
||||
|
||||
@ -38,12 +38,12 @@ function NewDeploymentMenu({ appInstanceId, availableEnvs }: {
|
||||
'hover:bg-components-button-primary-bg-hover',
|
||||
)}
|
||||
>
|
||||
<span className="i-ri-rocket-line h-3.5 w-3.5" />
|
||||
<span className="i-ri-rocket-line size-3.5" />
|
||||
{t('deployTab.newDeployment')}
|
||||
<span className="i-ri-arrow-down-s-line h-3.5 w-3.5" />
|
||||
<span className="i-ri-arrow-down-s-line size-3.5" />
|
||||
</DropdownMenuTrigger>
|
||||
{open && (
|
||||
<DropdownMenuContent placement="bottom-end" sideOffset={4} popupClassName="w-[220px]">
|
||||
<DropdownMenuContent placement="bottom-end" sideOffset={4} popupClassName="w-55">
|
||||
<DropdownMenuItem
|
||||
className="gap-2 px-3"
|
||||
onClick={() => {
|
||||
@ -99,7 +99,7 @@ export function DeployTab({ appInstanceId }: {
|
||||
const availableEnvs = environmentOptions.filter(env => env.id && !deployedEnvIds.has(env.id))
|
||||
|
||||
return (
|
||||
<div className="flex w-full max-w-[960px] flex-col gap-4 p-6">
|
||||
<div className="flex w-full max-w-240 flex-col gap-4 p-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="system-sm-semibold text-text-primary">
|
||||
{t('deployTab.envCount')}
|
||||
|
||||
@ -95,12 +95,12 @@ function DeploymentRowActions({ appInstanceId, envId, row }: {
|
||||
<DropdownMenu modal={false} open={menuOpen} onOpenChange={setMenuOpen}>
|
||||
<DropdownMenuTrigger
|
||||
aria-label={t('deployTab.moreActions')}
|
||||
className="flex h-7 w-7 items-center justify-center rounded-md text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary"
|
||||
className="flex size-7 items-center justify-center rounded-md text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary"
|
||||
>
|
||||
<span className="i-ri-more-line h-4 w-4" />
|
||||
<span className="i-ri-more-line size-4" />
|
||||
</DropdownMenuTrigger>
|
||||
{menuOpen && (
|
||||
<DropdownMenuContent placement="bottom-end" sideOffset={4} popupClassName="w-[200px]">
|
||||
<DropdownMenuContent placement="bottom-end" sideOffset={4} popupClassName="w-50">
|
||||
<DropdownMenuItem
|
||||
className="gap-2 px-3"
|
||||
onClick={handleRuntimeAction}
|
||||
@ -130,7 +130,7 @@ function DeploymentEnvironmentRow({ appInstanceId, row, isExpanded, onToggle }:
|
||||
const chevron = !isUndeployed && (
|
||||
<span
|
||||
className={cn(
|
||||
'i-ri-arrow-down-s-line h-4 w-4 shrink-0 text-text-tertiary transition-transform',
|
||||
'i-ri-arrow-down-s-line size-4 shrink-0 text-text-tertiary transition-transform',
|
||||
isExpanded && 'rotate-180',
|
||||
)}
|
||||
/>
|
||||
|
||||
@ -16,7 +16,7 @@ export function DeploymentStatusSummary({ row }: {
|
||||
if (isUndeployedDeploymentRow(row)) {
|
||||
return (
|
||||
<span className="inline-flex items-center gap-1.5 system-sm-medium text-text-tertiary">
|
||||
<span className="h-1.5 w-1.5 rounded-full bg-text-quaternary" />
|
||||
<span className="size-1.5 rounded-full bg-text-quaternary" />
|
||||
{t('status.notDeployed')}
|
||||
</span>
|
||||
)
|
||||
@ -27,7 +27,7 @@ export function DeploymentStatusSummary({ row }: {
|
||||
if (status === 'deploying') {
|
||||
return (
|
||||
<span className="inline-flex items-center gap-1.5 system-sm-medium text-util-colors-blue-blue-700">
|
||||
<span className="i-ri-loader-4-line h-3.5 w-3.5 animate-spin" />
|
||||
<span className="i-ri-loader-4-line size-3.5 animate-spin" />
|
||||
{t('deployTab.status.deployingRelease', { release: releaseLabel(activeRelease(row)) })}
|
||||
</span>
|
||||
)
|
||||
@ -37,7 +37,7 @@ export function DeploymentStatusSummary({ row }: {
|
||||
const hasRunningRelease = !!activeRelease(row)?.id
|
||||
return (
|
||||
<span className="inline-flex items-center gap-1.5 system-sm-medium text-util-colors-warning-warning-700">
|
||||
<span className="i-ri-alert-line h-3.5 w-3.5" />
|
||||
<span className="i-ri-alert-line size-3.5" />
|
||||
{t(hasRunningRelease ? 'deployTab.status.runningWithFailed' : 'deployTab.status.deployFailed')}
|
||||
</span>
|
||||
)
|
||||
@ -45,7 +45,7 @@ export function DeploymentStatusSummary({ row }: {
|
||||
|
||||
return (
|
||||
<span className="inline-flex items-center gap-1.5 system-sm-medium text-util-colors-green-green-700">
|
||||
<span className="h-1.5 w-1.5 rounded-full bg-util-colors-green-green-500" />
|
||||
<span className="size-1.5 rounded-full bg-util-colors-green-green-500" />
|
||||
{t('status.ready')}
|
||||
</span>
|
||||
)
|
||||
|
||||
@ -129,7 +129,7 @@ export function DeploymentSidebar({
|
||||
ref={sidebarRef}
|
||||
className={cn(
|
||||
'flex shrink-0 flex-col border-r border-divider-burn bg-background-default-subtle transition-all',
|
||||
expand ? 'w-[216px]' : 'w-14',
|
||||
expand ? 'w-54' : 'w-14',
|
||||
)}
|
||||
>
|
||||
<div className={cn('shrink-0', expand ? 'p-2' : 'p-1')}>
|
||||
@ -178,7 +178,7 @@ export function DeploymentSidebar({
|
||||
/>
|
||||
{!isMobile && isHoveringSidebar && (
|
||||
<ToggleButton
|
||||
className="absolute top-[-3.5px] -right-3 z-20"
|
||||
className="absolute -top-1 -right-3 z-20"
|
||||
expand={expand}
|
||||
handleToggle={toggleSidebarMode}
|
||||
/>
|
||||
|
||||
@ -35,7 +35,7 @@ export function InstanceDetail({ appInstanceId, children }: {
|
||||
if (!resolvedAppInstanceId && overviewQuery.isLoading) {
|
||||
return (
|
||||
<div className="flex h-full items-center justify-center bg-background-body">
|
||||
<span className="h-6 w-6 animate-spin rounded-full border-2 border-components-panel-border border-t-transparent" />
|
||||
<span className="size-6 animate-spin rounded-full border-2 border-components-panel-border border-t-transparent" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -45,7 +45,7 @@ export function InstanceDetail({ appInstanceId, children }: {
|
||||
<div className="flex h-full flex-col items-center justify-center gap-3 bg-background-body">
|
||||
<div className="title-xl-semi-bold text-text-primary">{t('detail.notFound')}</div>
|
||||
<Button nativeButton={false} variant="secondary" render={<Link href="/deployments" />}>
|
||||
<span aria-hidden className="i-ri-arrow-left-line h-4 w-4" />
|
||||
<span aria-hidden className="i-ri-arrow-left-line size-4" />
|
||||
{t('detail.backToInstances')}
|
||||
</Button>
|
||||
</div>
|
||||
@ -54,7 +54,7 @@ export function InstanceDetail({ appInstanceId, children }: {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="relative flex h-full overflow-hidden rounded-t-2xl shadow-[0_0_5px_rgba(0,0,0,0.05),0_0_2px_-1px_rgba(0,0,0,0.03)]">
|
||||
<div className="relative flex h-full overflow-hidden rounded-t-2xl shadow-xs">
|
||||
<DeploymentSidebar
|
||||
app={app}
|
||||
/>
|
||||
|
||||
@ -54,7 +54,7 @@ function AccessOverviewRow({ label, enabled, hint, meta }: AccessOverviewRowProp
|
||||
)}
|
||||
>
|
||||
<span className={cn(
|
||||
'h-1.5 w-1.5 rounded-full',
|
||||
'size-1.5 rounded-full',
|
||||
enabled ? 'bg-util-colors-green-green-500' : 'bg-text-quaternary',
|
||||
)}
|
||||
/>
|
||||
@ -147,14 +147,14 @@ function DeploymentStatusSection({ appInstanceId }: {
|
||||
action={(
|
||||
<Button nativeButton={false} size="small" variant="secondary" render={<Link href={`/deployments/${appInstanceId}/deploy`} />}>
|
||||
{t('overview.viewDeployments')}
|
||||
<span className="i-ri-arrow-right-up-line h-3.5 w-3.5" />
|
||||
<span className="i-ri-arrow-right-up-line size-3.5" />
|
||||
</Button>
|
||||
)}
|
||||
>
|
||||
{deployments.length === 0
|
||||
? (
|
||||
<div className="flex flex-col items-center gap-3 rounded-lg border border-dashed border-components-panel-border bg-components-panel-bg-blur px-4 py-8 text-center">
|
||||
<span className="i-ri-rocket-line h-5 w-5 text-text-quaternary" />
|
||||
<span className="i-ri-rocket-line size-5 text-text-quaternary" />
|
||||
<div className="system-sm-regular text-text-tertiary">
|
||||
{releaseRows.length === 0
|
||||
? t(canCreateRelease ? 'overview.noReleaseYet' : 'overview.noReleaseSourceUnavailable')
|
||||
@ -221,7 +221,7 @@ function AccessStatusSection({ appInstanceId }: {
|
||||
action={(
|
||||
<Button nativeButton={false} size="small" variant="secondary" render={<Link href={`/deployments/${appInstanceId}/access`} />}>
|
||||
{t('overview.configureAccess')}
|
||||
<span className="i-ri-arrow-right-up-line h-3.5 w-3.5" />
|
||||
<span className="i-ri-arrow-right-up-line size-3.5" />
|
||||
</Button>
|
||||
)}
|
||||
>
|
||||
@ -255,7 +255,7 @@ export function OverviewTab({ appInstanceId }: {
|
||||
appInstanceId: string
|
||||
}) {
|
||||
return (
|
||||
<div className="flex w-full max-w-[960px] flex-col gap-5 p-6">
|
||||
<div className="flex w-full max-w-240 flex-col gap-5 p-6">
|
||||
<BasicInfoSection appInstanceId={appInstanceId} />
|
||||
<DeploymentStatusSection appInstanceId={appInstanceId} />
|
||||
<AccessStatusSection appInstanceId={appInstanceId} />
|
||||
|
||||
@ -80,7 +80,7 @@ function DeleteInstanceButton({
|
||||
</Button>
|
||||
|
||||
<AlertDialog open={showDeleteConfirm} onOpenChange={open => !open && setShowDeleteConfirm(false)}>
|
||||
<AlertDialogContent className="w-[520px]">
|
||||
<AlertDialogContent className="w-130">
|
||||
<div className="flex flex-col gap-3 px-6 pt-6 pb-2">
|
||||
<AlertDialogTitle className="title-2xl-semi-bold text-text-primary">
|
||||
{t('settings.deleteConfirmTitle')}
|
||||
@ -183,7 +183,7 @@ function SettingsForm({ app, settings }: SettingsFormProps) {
|
||||
type="text"
|
||||
value={name}
|
||||
onChange={e => setName(e.target.value)}
|
||||
className="flex h-8 items-center rounded-lg border-[0.5px] border-components-input-border-active bg-components-input-bg-normal px-3 text-[13px] font-medium text-text-secondary outline-hidden placeholder:text-text-quaternary"
|
||||
className="flex h-8 items-center rounded-lg border border-components-input-border-active bg-components-input-bg-normal px-3 system-sm-medium text-text-secondary outline-hidden placeholder:text-text-quaternary"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
@ -194,7 +194,7 @@ function SettingsForm({ app, settings }: SettingsFormProps) {
|
||||
id="settings-desc"
|
||||
value={description}
|
||||
onChange={e => setDescription(e.target.value)}
|
||||
className="min-h-[96px] rounded-lg border-[0.5px] border-components-input-border-active bg-components-input-bg-normal px-3 py-2 text-[13px] text-text-secondary outline-hidden placeholder:text-text-quaternary"
|
||||
className="min-h-24 rounded-lg border border-components-input-border-active bg-components-input-bg-normal px-3 py-2 system-sm-regular text-text-secondary outline-hidden placeholder:text-text-quaternary"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex justify-end gap-2">
|
||||
@ -276,7 +276,7 @@ export function SettingsTab({ appInstanceId }: {
|
||||
appInstanceId: string
|
||||
}) {
|
||||
return (
|
||||
<div className="flex max-w-[640px] flex-col gap-5 p-6">
|
||||
<div className="flex max-w-160 flex-col gap-5 p-6">
|
||||
<SettingsFormSection appInstanceId={appInstanceId} />
|
||||
<DeleteInstanceControlSection appInstanceId={appInstanceId} />
|
||||
</div>
|
||||
|
||||
@ -57,12 +57,12 @@ function CreateReleaseControl({ appInstanceId, canCreateRelease }: {
|
||||
disabled={!canCreateRelease}
|
||||
onClick={() => setIsCreating(true)}
|
||||
>
|
||||
<span className="i-ri-add-line h-3.5 w-3.5" />
|
||||
<span className="i-ri-add-line size-3.5" />
|
||||
{t('versions.createRelease')}
|
||||
</Button>
|
||||
|
||||
<Dialog open={isCreating} onOpenChange={setIsCreating}>
|
||||
<DialogContent className="w-[560px] overflow-hidden p-0">
|
||||
<DialogContent className="w-140 overflow-hidden p-0">
|
||||
<DialogCloseButton />
|
||||
<form
|
||||
onSubmit={(event) => {
|
||||
@ -114,7 +114,7 @@ function CreateReleaseControl({ appInstanceId, canCreateRelease }: {
|
||||
name="description"
|
||||
placeholder={t('versions.releaseDescriptionPlaceholder')}
|
||||
maxLength={512}
|
||||
className="min-h-[96px] w-full resize-none appearance-none rounded-md border border-transparent bg-components-input-bg-normal p-2 system-sm-regular text-components-input-text-filled caret-primary-600 outline-hidden placeholder:text-components-input-text-placeholder hover:border-components-input-border-hover hover:bg-components-input-bg-hover focus:border-components-input-border-active focus:bg-components-input-bg-active focus:shadow-xs"
|
||||
className="min-h-24 w-full resize-none appearance-none rounded-md border border-transparent bg-components-input-bg-normal p-2 system-sm-regular text-components-input-text-filled caret-primary-600 outline-hidden placeholder:text-components-input-text-placeholder hover:border-components-input-border-hover hover:bg-components-input-bg-hover focus:border-components-input-border-active focus:bg-components-input-bg-active focus:shadow-xs"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -135,7 +135,7 @@ function CreateReleaseControl({ appInstanceId, canCreateRelease }: {
|
||||
<Button
|
||||
type="submit"
|
||||
variant="primary"
|
||||
className="min-w-[88px]"
|
||||
className="min-w-22"
|
||||
disabled={!canCreateRelease || createRelease.isPending}
|
||||
>
|
||||
{createRelease.isPending ? t('versions.creating') : t('versions.create')}
|
||||
@ -174,7 +174,7 @@ export function VersionsTab({ appInstanceId }: {
|
||||
const canCreateRelease = overview?.instance?.canCreateRelease ?? true
|
||||
|
||||
return (
|
||||
<div className="flex w-full max-w-[960px] flex-col gap-4 p-6">
|
||||
<div className="flex w-full max-w-240 flex-col gap-4 p-6">
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<div className="system-sm-semibold text-text-primary">
|
||||
{t('versions.releaseHistory')}
|
||||
|
||||
@ -53,10 +53,10 @@ export function DeployReleaseMenu({ appInstanceId, releaseId }: {
|
||||
)}
|
||||
>
|
||||
{t('versions.deploy')}
|
||||
<span className="i-ri-arrow-down-s-line h-3.5 w-3.5" />
|
||||
<span className="i-ri-arrow-down-s-line size-3.5" />
|
||||
</DropdownMenuTrigger>
|
||||
{open && (
|
||||
<DropdownMenuContent placement="bottom-end" sideOffset={4} popupClassName="w-[220px]">
|
||||
<DropdownMenuContent placement="bottom-end" sideOffset={4} popupClassName="w-55">
|
||||
{environments.map((env) => {
|
||||
const envId = env.id!
|
||||
const row = deploymentRows.find(item => environmentId(item.environment) === envId)
|
||||
|
||||
@ -28,10 +28,10 @@ export function DeployedToBadge({ item }: {
|
||||
)}
|
||||
>
|
||||
{item.state === 'deploying'
|
||||
? <span className="i-ri-loader-4-line h-3.5 w-3.5 animate-spin" />
|
||||
? <span className="i-ri-loader-4-line size-3.5 animate-spin" />
|
||||
: item.state === 'failed'
|
||||
? <span className="i-ri-alert-line h-3.5 w-3.5" />
|
||||
: <span className="h-1.5 w-1.5 rounded-full bg-current" />}
|
||||
? <span className="i-ri-alert-line size-3.5" />
|
||||
: <span className="size-1.5 rounded-full bg-current" />}
|
||||
{item.environmentName}
|
||||
</span>
|
||||
)}
|
||||
|
||||
@ -32,7 +32,7 @@ function getEnvironmentFilterOption(env: DeploymentEnvironmentOption & { id: str
|
||||
return {
|
||||
value: env.id,
|
||||
text: env.name || env.id,
|
||||
icon: <span className="i-ri-stack-line h-[14px] w-[14px]" />,
|
||||
icon: <span className="i-ri-stack-line size-[14px]" />,
|
||||
disabled: env.deployable === false,
|
||||
disabledReason: env.disabledReason,
|
||||
}
|
||||
@ -53,13 +53,13 @@ export function EnvironmentFilter() {
|
||||
{
|
||||
value: 'all',
|
||||
text: t('filter.allEnvs'),
|
||||
icon: <span className="i-ri-apps-2-line h-[14px] w-[14px]" />,
|
||||
icon: <span className="i-ri-apps-2-line size-[14px]" />,
|
||||
},
|
||||
...environments.map(getEnvironmentFilterOption),
|
||||
{
|
||||
value: 'not-deployed',
|
||||
text: t('filter.notDeployed'),
|
||||
icon: <span className="i-ri-inbox-line h-[14px] w-[14px]" />,
|
||||
icon: <span className="i-ri-inbox-line size-[14px]" />,
|
||||
},
|
||||
]
|
||||
const selectedOption = filterOptions.find(option => option.value === activeFilter) ?? filterOptions[0]
|
||||
@ -68,25 +68,25 @@ export function EnvironmentFilter() {
|
||||
<DropdownMenu modal={false} open={open} onOpenChange={setOpen}>
|
||||
<DropdownMenuTrigger
|
||||
className={cn(
|
||||
'flex h-8 cursor-pointer items-center gap-1 rounded-lg border-[0.5px] border-transparent bg-components-input-bg-normal px-2 text-left select-none',
|
||||
'flex h-8 cursor-pointer items-center gap-1 rounded-lg border border-transparent bg-components-input-bg-normal px-2 text-left select-none',
|
||||
open && 'shadow-xs',
|
||||
)}
|
||||
>
|
||||
<div className="p-px text-text-tertiary">
|
||||
{selectedOption?.icon}
|
||||
</div>
|
||||
<div className="max-w-[160px] min-w-0 truncate text-[13px] leading-[18px] text-text-secondary">
|
||||
<div className="max-w-40 min-w-0 truncate system-sm-regular text-text-secondary">
|
||||
{selectedOption?.text}
|
||||
</div>
|
||||
<div className="shrink-0 p-px">
|
||||
<span className={cn('i-ri-arrow-down-s-line h-3.5 w-3.5 text-text-tertiary transition-transform', open && 'rotate-180')} />
|
||||
<span className={cn('i-ri-arrow-down-s-line size-3.5 text-text-tertiary transition-transform', open && 'rotate-180')} />
|
||||
</div>
|
||||
</DropdownMenuTrigger>
|
||||
{open && (
|
||||
<DropdownMenuContent
|
||||
placement="bottom-start"
|
||||
sideOffset={4}
|
||||
popupClassName="w-[240px] rounded-lg border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg backdrop-blur-[5px]"
|
||||
popupClassName="w-60 rounded-lg border border-components-panel-border bg-components-panel-bg-blur shadow-lg backdrop-blur-xs"
|
||||
>
|
||||
<div className="max-h-72 overflow-auto p-1">
|
||||
{filterOptions.map(option => (
|
||||
@ -101,16 +101,16 @@ export function EnvironmentFilter() {
|
||||
title={option.disabled ? option.disabledReason : undefined}
|
||||
aria-disabled={option.disabled}
|
||||
className={cn(
|
||||
'flex items-center gap-2 rounded-lg py-[6px] pr-2 pl-3 select-none',
|
||||
'flex items-center gap-2 rounded-lg py-1.5 pr-2 pl-3 select-none',
|
||||
option.disabled
|
||||
? 'cursor-not-allowed opacity-50'
|
||||
: 'cursor-pointer hover:bg-state-base-hover',
|
||||
)}
|
||||
>
|
||||
<span className="shrink-0 text-text-tertiary">{option.icon}</span>
|
||||
<span className="grow truncate text-sm leading-5 text-text-tertiary">{option.text}</span>
|
||||
<span className="grow truncate text-sm/5 text-text-tertiary">{option.text}</span>
|
||||
{option.value === activeFilter && (
|
||||
<span className="i-custom-vender-line-general-check h-4 w-4 shrink-0 text-text-secondary" />
|
||||
<span className="i-custom-vender-line-general-check size-4 shrink-0 text-text-secondary" />
|
||||
)}
|
||||
</DropdownMenuItem>
|
||||
))}
|
||||
|
||||
@ -27,7 +27,7 @@ function DeploymentsSearchInput() {
|
||||
<Input
|
||||
showLeftIcon
|
||||
showClearIcon
|
||||
wrapperClassName="w-[200px]"
|
||||
wrapperClassName="w-50"
|
||||
placeholder={t('filter.searchPlaceholder')}
|
||||
value={keywords}
|
||||
onChange={e => handleKeywordsChange(e.target.value)}
|
||||
@ -72,7 +72,7 @@ function DeploymentsList() {
|
||||
return (
|
||||
<div className="relative flex h-0 shrink-0 grow flex-col overflow-y-auto bg-background-body">
|
||||
<DeploymentsListControls />
|
||||
<div className="relative grid grow grid-cols-1 content-start gap-4 px-12 pt-2 2k:grid-cols-6 sm:grid-cols-1 md:grid-cols-2 xl:grid-cols-4 2xl:grid-cols-5">
|
||||
<div className="relative grid grow grid-cols-1 content-start gap-4 px-12 pt-2 2k:grid-cols-6 md:grid-cols-2 xl:grid-cols-4 2xl:grid-cols-5">
|
||||
<NewInstanceCard />
|
||||
{apps.map(app => (
|
||||
<InstanceCard
|
||||
|
||||
@ -22,16 +22,16 @@ function NewInstanceAction({ icon, label, disabled, onClick }: NewInstanceAction
|
||||
disabled={disabled}
|
||||
title={disabled ? t('newInstance.comingSoon') : undefined}
|
||||
className={cn(
|
||||
'mb-1 flex w-full items-center rounded-lg px-6 py-[7px] text-left text-[13px] leading-[18px] font-medium text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary',
|
||||
'mb-1 flex h-8 w-full items-center gap-2 rounded-lg px-6 text-left system-sm-medium text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary',
|
||||
disabled
|
||||
? 'cursor-not-allowed opacity-50 hover:bg-transparent hover:text-text-tertiary'
|
||||
: 'cursor-pointer',
|
||||
)}
|
||||
>
|
||||
<span aria-hidden className={cn('mr-2 h-4 w-4 shrink-0', icon)} />
|
||||
<span aria-hidden className={cn('size-4 shrink-0', icon)} />
|
||||
<span className="min-w-0 flex-1 truncate">{label}</span>
|
||||
{disabled && (
|
||||
<span className="ml-2 shrink-0 rounded-md bg-state-base-hover px-1.5 system-2xs-medium text-text-tertiary">
|
||||
<span className="shrink-0 rounded-md bg-state-base-hover px-1.5 system-2xs-medium text-text-tertiary">
|
||||
{t('newInstance.comingSoon')}
|
||||
</span>
|
||||
)}
|
||||
@ -56,9 +56,9 @@ export function NewInstanceCard() {
|
||||
const { t } = useTranslation('deployments')
|
||||
|
||||
return (
|
||||
<div className="relative col-span-1 inline-flex h-[160px] flex-col justify-between rounded-xl border-[0.5px] border-components-card-border bg-components-card-bg">
|
||||
<div className="relative col-span-1 inline-flex h-40 flex-col justify-between rounded-xl border border-components-card-border bg-components-card-bg">
|
||||
<div className="grow rounded-t-xl p-2">
|
||||
<div className="px-6 pt-2 pb-1 text-xs leading-[18px] font-medium text-text-tertiary">
|
||||
<div className="px-6 pt-2 pb-1 text-xs/[18px] font-medium text-text-tertiary">
|
||||
{t('newInstance.title')}
|
||||
</div>
|
||||
<CreateFromStudioAction />
|
||||
|
||||
@ -94,8 +94,8 @@ export function DeploymentsNav() {
|
||||
return (
|
||||
<Nav
|
||||
isApp={false}
|
||||
icon={<span aria-hidden className="i-ri-rocket-line h-4 w-4" />}
|
||||
activeIcon={<span aria-hidden className="i-ri-rocket-fill h-4 w-4" />}
|
||||
icon={<span aria-hidden className="i-ri-rocket-line size-4" />}
|
||||
activeIcon={<span aria-hidden className="i-ri-rocket-fill size-4" />}
|
||||
text={t('menus.deployments', { ns: 'common' })}
|
||||
activeSegment="deployments"
|
||||
link="/deployments"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user