2025-12-01 18:56:16 +00:00
|
|
|
import * as plugins from '../../plugins.js';
|
|
|
|
|
import {
|
|
|
|
|
html,
|
|
|
|
|
css,
|
2025-12-01 20:03:34 +00:00
|
|
|
cssManager,
|
2025-12-01 18:56:16 +00:00
|
|
|
type TemplateResult,
|
|
|
|
|
} from '@design.estate/dees-element';
|
|
|
|
|
|
|
|
|
|
import { accountDesignTokens } from './sharedstyles.js';
|
|
|
|
|
import * as accountStateModule from '../../states/accountstate.js';
|
|
|
|
|
|
2025-12-01 20:03:34 +00:00
|
|
|
export interface IOrgSelectResult {
|
|
|
|
|
org: plugins.idpInterfaces.data.IOrganization;
|
|
|
|
|
path: string;
|
2025-12-01 18:56:16 +00:00
|
|
|
}
|
|
|
|
|
|
2025-12-01 20:03:34 +00:00
|
|
|
const modalStyles = css`
|
|
|
|
|
.org-list {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
}
|
2025-12-01 18:56:16 +00:00
|
|
|
|
2025-12-01 20:03:34 +00:00
|
|
|
.org-item {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 12px;
|
|
|
|
|
padding: 14px 20px;
|
|
|
|
|
border-bottom: 1px solid var(--dees-color-line);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
transition: background 0.15s ease;
|
|
|
|
|
}
|
2025-12-01 18:56:16 +00:00
|
|
|
|
2025-12-01 20:03:34 +00:00
|
|
|
.org-item:last-child {
|
|
|
|
|
border-bottom: none;
|
|
|
|
|
}
|
2025-12-01 18:56:16 +00:00
|
|
|
|
2025-12-01 20:03:34 +00:00
|
|
|
.org-item:hover {
|
|
|
|
|
background: var(--dees-color-softBackground);
|
|
|
|
|
}
|
2025-12-01 18:56:16 +00:00
|
|
|
|
2025-12-01 20:03:34 +00:00
|
|
|
.org-icon {
|
|
|
|
|
width: 40px;
|
|
|
|
|
height: 40px;
|
|
|
|
|
border-radius: 10px;
|
|
|
|
|
background: var(--dees-color-softBackground);
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
2025-12-01 18:56:16 +00:00
|
|
|
|
2025-12-01 20:03:34 +00:00
|
|
|
.org-item:hover .org-icon {
|
|
|
|
|
background: var(--dees-color-line);
|
|
|
|
|
}
|
2025-12-01 18:56:16 +00:00
|
|
|
|
2025-12-01 20:03:34 +00:00
|
|
|
.org-icon dees-icon {
|
|
|
|
|
opacity: 0.7;
|
|
|
|
|
}
|
2025-12-01 18:56:16 +00:00
|
|
|
|
2025-12-01 20:03:34 +00:00
|
|
|
.org-info {
|
|
|
|
|
flex: 1;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
}
|
2025-12-01 18:56:16 +00:00
|
|
|
|
2025-12-01 20:03:34 +00:00
|
|
|
.org-name {
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
margin-bottom: 2px;
|
|
|
|
|
color: var(--dees-color-text);
|
|
|
|
|
}
|
2025-12-01 18:56:16 +00:00
|
|
|
|
2025-12-01 20:03:34 +00:00
|
|
|
.org-slug {
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
color: var(--dees-color-muted);
|
|
|
|
|
}
|
2025-12-01 18:56:16 +00:00
|
|
|
|
2025-12-01 20:03:34 +00:00
|
|
|
.org-arrow {
|
|
|
|
|
opacity: 0.5;
|
|
|
|
|
}
|
2025-12-01 18:56:16 +00:00
|
|
|
|
2025-12-01 20:03:34 +00:00
|
|
|
.empty-state {
|
|
|
|
|
text-align: center;
|
|
|
|
|
padding: 40px 24px;
|
|
|
|
|
color: var(--dees-color-muted);
|
|
|
|
|
}
|
2025-12-01 18:56:16 +00:00
|
|
|
|
2025-12-01 20:03:34 +00:00
|
|
|
.empty-state dees-icon {
|
|
|
|
|
font-size: 40px;
|
|
|
|
|
opacity: 0.5;
|
|
|
|
|
margin-bottom: 12px;
|
2025-12-01 18:56:16 +00:00
|
|
|
}
|
|
|
|
|
|
2025-12-01 20:03:34 +00:00
|
|
|
.empty-state p {
|
|
|
|
|
margin: 0 0 16px 0;
|
|
|
|
|
font-size: 14px;
|
2025-12-01 18:56:16 +00:00
|
|
|
}
|
|
|
|
|
|
2025-12-01 20:03:34 +00:00
|
|
|
.description {
|
|
|
|
|
color: var(--dees-color-muted);
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
margin-bottom: 16px;
|
|
|
|
|
padding: 0 20px;
|
2025-12-01 18:56:16 +00:00
|
|
|
}
|
2025-12-01 20:03:34 +00:00
|
|
|
`;
|
2025-12-01 18:56:16 +00:00
|
|
|
|
2025-12-01 20:03:34 +00:00
|
|
|
export class OrgSelectModal {
|
|
|
|
|
public static async show(options: {
|
2025-12-01 18:56:16 +00:00
|
|
|
targetPath: string;
|
|
|
|
|
title?: string;
|
|
|
|
|
description?: string;
|
2025-12-01 20:03:34 +00:00
|
|
|
}): Promise<IOrgSelectResult | null> {
|
|
|
|
|
const title = options.title || 'Select Organization';
|
|
|
|
|
const description = options.description || 'Choose an organization to continue.';
|
2025-12-01 18:56:16 +00:00
|
|
|
|
|
|
|
|
// Load organizations from state
|
|
|
|
|
const state = accountStateModule.accountState.getState();
|
2025-12-01 20:03:34 +00:00
|
|
|
const organizations = state.organizations;
|
2025-12-01 18:56:16 +00:00
|
|
|
|
2025-12-01 20:03:34 +00:00
|
|
|
return new Promise<IOrgSelectResult | null>((resolve) => {
|
|
|
|
|
let modal: plugins.deesCatalog.DeesModal | null = null;
|
|
|
|
|
let resolved = false;
|
2025-12-01 18:56:16 +00:00
|
|
|
|
2025-12-01 20:03:34 +00:00
|
|
|
const handleSelectOrg = (org: plugins.idpInterfaces.data.IOrganization) => {
|
|
|
|
|
if (resolved) return;
|
|
|
|
|
resolved = true;
|
2025-12-01 18:56:16 +00:00
|
|
|
|
2025-12-01 20:03:34 +00:00
|
|
|
accountStateModule.accountState.dispatchAction(accountStateModule.setSelectedOrg, org);
|
|
|
|
|
const path = options.targetPath.replace(':orgName', org.data.slug);
|
2025-12-01 18:56:16 +00:00
|
|
|
|
2025-12-01 20:03:34 +00:00
|
|
|
modal?.destroy();
|
|
|
|
|
resolve({ org, path });
|
|
|
|
|
};
|
2025-12-01 18:56:16 +00:00
|
|
|
|
2025-12-01 20:03:34 +00:00
|
|
|
const handleCreateOrg = async () => {
|
|
|
|
|
if (resolved) return;
|
|
|
|
|
resolved = true;
|
|
|
|
|
modal?.destroy();
|
2025-12-01 18:56:16 +00:00
|
|
|
|
2025-12-01 20:03:34 +00:00
|
|
|
// Import dynamically to avoid circular dependency
|
|
|
|
|
const { CreateOrgModal } = await import('./create-org-modal.js');
|
|
|
|
|
const createdOrg = await CreateOrgModal.show();
|
2025-12-01 18:56:16 +00:00
|
|
|
|
2025-12-01 20:03:34 +00:00
|
|
|
if (createdOrg) {
|
|
|
|
|
const path = options.targetPath.replace(':orgName', createdOrg.data.slug);
|
|
|
|
|
resolve({ org: createdOrg, path });
|
|
|
|
|
} else {
|
|
|
|
|
resolve(null);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const renderOrgList = (): TemplateResult => {
|
|
|
|
|
return html`
|
|
|
|
|
<style>${modalStyles}</style>
|
|
|
|
|
<div class="description">${description}</div>
|
|
|
|
|
<div class="org-list">
|
|
|
|
|
${organizations.map((org) => html`
|
|
|
|
|
<div class="org-item" @click=${() => handleSelectOrg(org)}>
|
|
|
|
|
<div class="org-icon">
|
|
|
|
|
<dees-icon .icon=${'lucide:building2'}></dees-icon>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="org-info">
|
|
|
|
|
<div class="org-name">${org.data.name}</div>
|
|
|
|
|
<div class="org-slug">${org.data.slug}</div>
|
|
|
|
|
</div>
|
|
|
|
|
<dees-icon class="org-arrow" .icon=${'lucide:chevron-right'}></dees-icon>
|
|
|
|
|
</div>
|
|
|
|
|
`)}
|
|
|
|
|
</div>
|
|
|
|
|
`;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const renderEmptyState = (): TemplateResult => {
|
|
|
|
|
return html`
|
|
|
|
|
<style>${modalStyles}</style>
|
|
|
|
|
<div class="empty-state">
|
|
|
|
|
<dees-icon .icon=${'lucide:building2'}></dees-icon>
|
|
|
|
|
<p>You don't have any organizations yet.</p>
|
|
|
|
|
<dees-button @clicked=${handleCreateOrg}>
|
|
|
|
|
<dees-icon .icon=${'lucide:plus'} slot="iconLeft"></dees-icon>
|
|
|
|
|
Create Organization
|
|
|
|
|
</dees-button>
|
|
|
|
|
</div>
|
|
|
|
|
`;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const content = organizations.length === 0 ? renderEmptyState() : renderOrgList();
|
|
|
|
|
|
|
|
|
|
plugins.deesCatalog.DeesModal.createAndShow({
|
|
|
|
|
heading: title,
|
|
|
|
|
content,
|
|
|
|
|
menuOptions: [
|
|
|
|
|
{
|
|
|
|
|
name: 'Cancel',
|
|
|
|
|
action: async (modalRef) => {
|
|
|
|
|
if (!resolved) {
|
|
|
|
|
resolved = true;
|
|
|
|
|
resolve(null);
|
|
|
|
|
}
|
|
|
|
|
modalRef.destroy();
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
width: 420,
|
|
|
|
|
}).then((m) => {
|
|
|
|
|
modal = m;
|
|
|
|
|
});
|
|
|
|
|
});
|
2025-12-01 18:56:16 +00:00
|
|
|
}
|
|
|
|
|
}
|