mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-09 18:15:50 +00:00
Converge security auth actions on shared Button primitive
This commit is contained in:
@@ -572,6 +572,10 @@ not a replacement status card, CTA band, or page-local nested card.
|
||||
cloud-paid surfaces own Suspend, Activate, Reload, tenant state, and mutation
|
||||
semantics, while `Button` owns the row-action chrome through the secondary
|
||||
`sm` and `xs` sizes.
|
||||
Security authentication settings actions follow the same boundary:
|
||||
security/privacy owns auth setup, password-change, credential-rotation, and
|
||||
read-only semantics, while `Button` owns the warning, primary, secondary,
|
||||
and settings-action chrome.
|
||||
If a new surface needs a variant that the shared primitive does not expose,
|
||||
extend the primitive and registry guard rather than adding a page-local
|
||||
class string.
|
||||
|
||||
@@ -221,6 +221,11 @@ controls as normal product settings.
|
||||
primitive. Security/privacy owns scope authority, wildcard behavior, preset
|
||||
membership, and custom scope toggles; frontend-primitives owns active and
|
||||
inactive pill tone, focus, disabled treatment, and pressed-state wiring.
|
||||
6c. Keep authentication setup, password-change, and credential-rotation actions
|
||||
on the shared `Button` primitive. Security/privacy owns the auth authority,
|
||||
setup/rotation semantics, and read-only capability state;
|
||||
frontend-primitives owns warning, primary, secondary, focus, disabled, and
|
||||
settings-action chrome.
|
||||
6. Keep the shared storage-directory and secure storage-file hardening helper aligned with the crypto manager plus control-plane magic-link key and store handling whenever runtime data-root ownership assumptions change.
|
||||
7. Keep auth-env ingestion, hosted commercial base URL validation, and shared
|
||||
fingerprint-verifier TLS defaults aligned whenever runtime auth loading,
|
||||
|
||||
@@ -970,6 +970,7 @@
|
||||
{ "path": "src/components/Settings/ReportingPanel.tsx" },
|
||||
{ "path": "src/components/Settings/ResourcePicker.tsx" },
|
||||
{ "path": "src/components/Settings/SelfHostedCommercialRecoverySection.tsx" },
|
||||
{ "path": "src/components/Settings/SecurityAuthPanel.tsx" },
|
||||
{ "path": "src/components/Settings/SSOProvidersPanel.tsx" },
|
||||
{ "path": "src/components/UpdateConfirmationModal.tsx" },
|
||||
{ "path": "src/components/UpdateProgressModal.tsx" },
|
||||
@@ -1038,6 +1039,14 @@
|
||||
"min-h-10 sm:min-h-9 px-4 py-2.5 text-sm font-medium rounded-md border border-border text-base-content hover:bg-surface-hover transition-colors disabled:opacity-60 disabled:cursor-not-allowed"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "src/components/Settings/SecurityAuthPanel.tsx",
|
||||
"patterns": [
|
||||
"w-full sm:w-auto px-3 py-2 text-xs font-medium rounded-md border border-amber-300 text-amber-800 bg-amber-100 hover:bg-amber-200 transition-colors dark:border-amber-700 dark:text-amber-200 dark:bg-amber-900 dark:hover:bg-amber-800",
|
||||
"w-full sm:w-auto min-h-10 sm:min-h-10 px-4 py-2.5 text-sm font-medium bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors",
|
||||
"w-full sm:w-auto min-h-10 sm:min-h-10 px-4 py-2.5 text-sm font-medium border border-border text-base-content rounded-md hover:bg-surface-hover transition-colors"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "src/components/Settings/SSOProvidersPanel.tsx",
|
||||
"patterns": [
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Component, Show, Accessor, Setter } from 'solid-js';
|
||||
import { Button } from '@/components/shared/Button';
|
||||
import { CalloutCard } from '@/components/shared/CalloutCard';
|
||||
import SettingsPanel from '@/components/shared/SettingsPanel';
|
||||
import { Toggle } from '@/components/shared/Toggle';
|
||||
@@ -75,14 +76,15 @@ export const SecurityAuthPanel: Component<SecurityAuthPanelProps> = (props) => {
|
||||
{SECURITY_AUTH_DISABLED_READ_ONLY_MESSAGE}
|
||||
</p>
|
||||
</Show>
|
||||
<button
|
||||
type="button"
|
||||
<Button
|
||||
variant="warning"
|
||||
size="settingsActionXs"
|
||||
class="w-full sm:w-auto"
|
||||
onClick={() => props.setShowQuickSecuritySetup(!props.showQuickSecuritySetup())}
|
||||
disabled={!props.canManage}
|
||||
class="w-full sm:w-auto px-3 py-2 text-xs font-medium rounded-md border border-amber-300 text-amber-800 bg-amber-100 hover:bg-amber-200 transition-colors dark:border-amber-700 dark:text-amber-200 dark:bg-amber-900 dark:hover:bg-amber-800"
|
||||
>
|
||||
{SECURITY_AUTH_SETUP_LABEL}
|
||||
</button>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<Show when={props.canManage && props.showQuickSecuritySetup()}>
|
||||
@@ -107,26 +109,28 @@ export const SecurityAuthPanel: Component<SecurityAuthPanelProps> = (props) => {
|
||||
</div>
|
||||
</Show>
|
||||
<div class="flex flex-col gap-2 sm:flex-row sm:flex-wrap sm:items-center">
|
||||
<button
|
||||
type="button"
|
||||
<Button
|
||||
variant="primary"
|
||||
size="settingsAction"
|
||||
class="w-full sm:w-auto"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
props.setShowPasswordModal(true);
|
||||
}}
|
||||
disabled={!props.canManage}
|
||||
class="w-full sm:w-auto min-h-10 sm:min-h-10 px-4 py-2.5 text-sm font-medium bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors"
|
||||
>
|
||||
Change password
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
</Button>
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="settingsAction"
|
||||
class="w-full sm:w-auto"
|
||||
onClick={() => props.setShowQuickSecurityWizard(!props.showQuickSecurityWizard())}
|
||||
disabled={!props.canManage}
|
||||
class="w-full sm:w-auto min-h-10 sm:min-h-10 px-4 py-2.5 text-sm font-medium border border-border text-base-content rounded-md hover:bg-surface-hover transition-colors"
|
||||
>
|
||||
Rotate credentials
|
||||
</button>
|
||||
</Button>
|
||||
</div>
|
||||
<div class="text-xs text-muted">
|
||||
<span class="font-medium text-base-content">User:</span>{' '}
|
||||
|
||||
@@ -327,6 +327,22 @@ describe('settings architecture guardrails', () => {
|
||||
expect(ssoProvidersPanelSource).not.toContain(
|
||||
'text-blue-600 hover:underline flex items-center gap-1',
|
||||
);
|
||||
|
||||
expect(securityAuthPanelSource).toContain(
|
||||
"import { Button } from '@/components/shared/Button';",
|
||||
);
|
||||
expect(securityAuthPanelSource).toContain('variant="warning"');
|
||||
expect(securityAuthPanelSource).toContain('variant="primary"');
|
||||
expect(securityAuthPanelSource).toContain('size="settingsAction"');
|
||||
expect(securityAuthPanelSource).not.toContain(
|
||||
'w-full sm:w-auto px-3 py-2 text-xs font-medium rounded-md border border-amber-300',
|
||||
);
|
||||
expect(securityAuthPanelSource).not.toContain(
|
||||
'w-full sm:w-auto min-h-10 sm:min-h-10 px-4 py-2.5 text-sm font-medium bg-blue-600',
|
||||
);
|
||||
expect(securityAuthPanelSource).not.toContain(
|
||||
'w-full sm:w-auto min-h-10 sm:min-h-10 px-4 py-2.5 text-sm font-medium border border-border',
|
||||
);
|
||||
});
|
||||
|
||||
it('keeps settings callouts on the shared CalloutCard primitive', () => {
|
||||
|
||||
@@ -3098,6 +3098,7 @@ describe('shared primitive guardrails', () => {
|
||||
'src/components/Settings/ReportingPanel.tsx',
|
||||
'src/components/Settings/ResourcePicker.tsx',
|
||||
'src/components/Settings/SelfHostedCommercialRecoverySection.tsx',
|
||||
'src/components/Settings/SecurityAuthPanel.tsx',
|
||||
'src/components/Settings/SSOProvidersPanel.tsx',
|
||||
'src/components/UpdateConfirmationModal.tsx',
|
||||
'src/components/UpdateProgressModal.tsx',
|
||||
@@ -3167,6 +3168,14 @@ describe('shared primitive guardrails', () => {
|
||||
'min-h-10 sm:min-h-9 px-4 py-2.5 text-sm font-medium rounded-md border border-border text-base-content hover:bg-surface-hover transition-colors disabled:opacity-60 disabled:cursor-not-allowed',
|
||||
]),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
path: 'src/components/Settings/SecurityAuthPanel.tsx',
|
||||
patterns: expect.arrayContaining([
|
||||
'w-full sm:w-auto px-3 py-2 text-xs font-medium rounded-md border border-amber-300 text-amber-800 bg-amber-100 hover:bg-amber-200 transition-colors dark:border-amber-700 dark:text-amber-200 dark:bg-amber-900 dark:hover:bg-amber-800',
|
||||
'w-full sm:w-auto min-h-10 sm:min-h-10 px-4 py-2.5 text-sm font-medium bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors',
|
||||
'w-full sm:w-auto min-h-10 sm:min-h-10 px-4 py-2.5 text-sm font-medium border border-border text-base-content rounded-md hover:bg-surface-hover transition-colors',
|
||||
]),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
path: 'src/components/Settings/SSOProvidersPanel.tsx',
|
||||
patterns: expect.arrayContaining([
|
||||
@@ -3955,6 +3964,21 @@ describe('shared primitive guardrails', () => {
|
||||
expect(selfHostedCommercialRecoverySectionSource).not.toContain(
|
||||
'min-h-10 sm:min-h-9 px-4 py-2.5 text-sm font-medium rounded-md border border-border text-base-content hover:bg-surface-hover transition-colors disabled:opacity-60 disabled:cursor-not-allowed',
|
||||
);
|
||||
expect(securityAuthPanelSource).toContain('@/components/shared/Button');
|
||||
expect(securityAuthPanelSource).toContain('<Button');
|
||||
expect(securityAuthPanelSource).toContain('variant="warning"');
|
||||
expect(securityAuthPanelSource).toContain('variant="primary"');
|
||||
expect(securityAuthPanelSource).toContain('size="settingsAction"');
|
||||
expect(securityAuthPanelSource).toContain('size="settingsActionXs"');
|
||||
expect(securityAuthPanelSource).not.toContain(
|
||||
'w-full sm:w-auto px-3 py-2 text-xs font-medium rounded-md border border-amber-300 text-amber-800 bg-amber-100 hover:bg-amber-200 transition-colors dark:border-amber-700 dark:text-amber-200 dark:bg-amber-900 dark:hover:bg-amber-800',
|
||||
);
|
||||
expect(securityAuthPanelSource).not.toContain(
|
||||
'w-full sm:w-auto min-h-10 sm:min-h-10 px-4 py-2.5 text-sm font-medium bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors',
|
||||
);
|
||||
expect(securityAuthPanelSource).not.toContain(
|
||||
'w-full sm:w-auto min-h-10 sm:min-h-10 px-4 py-2.5 text-sm font-medium border border-border text-base-content rounded-md hover:bg-surface-hover transition-colors',
|
||||
);
|
||||
expect(discoveryTabSource).toContain('@/components/shared/Button');
|
||||
expect(discoveryTabSource).toContain('@/components/shared/CopyableCodeRow');
|
||||
expect(discoveryTabSource).toContain('CopyValueButton');
|
||||
|
||||
Reference in New Issue
Block a user