319 lines
7.0 KiB
TypeScript
319 lines
7.0 KiB
TypeScript
import * as plugins from '../../plugins.js';
|
|
import {
|
|
customElement,
|
|
DeesElement,
|
|
property,
|
|
html,
|
|
cssManager,
|
|
css,
|
|
state,
|
|
type TemplateResult,
|
|
} from '@design.estate/dees-element';
|
|
|
|
import { accountDesignTokens } from './sharedstyles.js';
|
|
import * as accountStateModule from '../../states/accountstate.js';
|
|
|
|
declare global {
|
|
interface HTMLElementTagNameMap {
|
|
'idp-org-select-modal': OrgSelectModal;
|
|
}
|
|
}
|
|
|
|
@customElement('idp-org-select-modal')
|
|
export class OrgSelectModal extends DeesElement {
|
|
@state()
|
|
accessor visible: boolean = false;
|
|
|
|
@state()
|
|
accessor organizations: plugins.idpInterfaces.data.IOrganization[] = [];
|
|
|
|
@state()
|
|
accessor targetPath: string = '';
|
|
|
|
@state()
|
|
accessor title: string = 'Select Organization';
|
|
|
|
@state()
|
|
accessor description: string = 'Choose an organization to continue.';
|
|
|
|
public static styles = [
|
|
cssManager.defaultStyles,
|
|
accountDesignTokens,
|
|
css`
|
|
:host {
|
|
display: none;
|
|
}
|
|
|
|
:host([visible]) {
|
|
display: block;
|
|
}
|
|
|
|
.overlay {
|
|
position: fixed;
|
|
inset: 0;
|
|
background: rgba(0, 0, 0, 0.8);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
z-index: 1000;
|
|
animation: fadeIn 0.15s ease;
|
|
}
|
|
|
|
@keyframes fadeIn {
|
|
from {
|
|
opacity: 0;
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
}
|
|
}
|
|
|
|
.modal {
|
|
background: #18181b;
|
|
border: 1px solid #27272a;
|
|
border-radius: 16px;
|
|
width: 100%;
|
|
max-width: 420px;
|
|
max-height: 90vh;
|
|
overflow-y: auto;
|
|
animation: slideIn 0.2s ease;
|
|
}
|
|
|
|
@keyframes slideIn {
|
|
from {
|
|
opacity: 0;
|
|
transform: translateY(-20px);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
}
|
|
|
|
.modal-header {
|
|
padding: 20px 24px;
|
|
border-bottom: 1px solid #27272a;
|
|
}
|
|
|
|
.modal-title {
|
|
font-size: 18px;
|
|
font-weight: 600;
|
|
margin: 0 0 4px 0;
|
|
color: #fafafa;
|
|
}
|
|
|
|
.modal-description {
|
|
font-size: 14px;
|
|
color: #71717a;
|
|
margin: 0;
|
|
}
|
|
|
|
.modal-body {
|
|
padding: 0;
|
|
}
|
|
|
|
.org-list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.org-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
padding: 14px 24px;
|
|
border-bottom: 1px solid #27272a;
|
|
cursor: pointer;
|
|
transition: background 0.15s ease;
|
|
}
|
|
|
|
.org-item:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.org-item:hover {
|
|
background: #27272a;
|
|
}
|
|
|
|
.org-icon {
|
|
width: 40px;
|
|
height: 40px;
|
|
border-radius: 10px;
|
|
background: #27272a;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.org-item:hover .org-icon {
|
|
background: #3f3f46;
|
|
}
|
|
|
|
.org-icon dees-icon {
|
|
opacity: 0.7;
|
|
}
|
|
|
|
.org-info {
|
|
flex: 1;
|
|
min-width: 0;
|
|
}
|
|
|
|
.org-name {
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
margin-bottom: 2px;
|
|
color: #fafafa;
|
|
}
|
|
|
|
.org-slug {
|
|
font-size: 12px;
|
|
color: #71717a;
|
|
}
|
|
|
|
.org-arrow {
|
|
opacity: 0.5;
|
|
}
|
|
|
|
.empty-state {
|
|
text-align: center;
|
|
padding: 40px 24px;
|
|
color: #71717a;
|
|
}
|
|
|
|
.empty-state dees-icon {
|
|
font-size: 40px;
|
|
opacity: 0.5;
|
|
margin-bottom: 12px;
|
|
}
|
|
|
|
.empty-state p {
|
|
margin: 0 0 16px 0;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.modal-footer {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
gap: 12px;
|
|
padding: 16px 24px;
|
|
border-top: 1px solid #27272a;
|
|
}
|
|
`,
|
|
];
|
|
|
|
public render(): TemplateResult {
|
|
if (!this.visible) {
|
|
return html``;
|
|
}
|
|
|
|
return html`
|
|
<div class="overlay" @click=${this.handleOverlayClick}>
|
|
<div class="modal" @click=${(e: Event) => e.stopPropagation()}>
|
|
<div class="modal-header">
|
|
<h2 class="modal-title">${this.title}</h2>
|
|
<p class="modal-description">${this.description}</p>
|
|
</div>
|
|
<div class="modal-body">
|
|
${this.organizations.length === 0
|
|
? this.renderEmptyState()
|
|
: this.renderOrgList()}
|
|
</div>
|
|
<div class="modal-footer">
|
|
<dees-button type="secondary" @clicked=${this.handleCancel}>
|
|
Cancel
|
|
</dees-button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
private renderOrgList(): TemplateResult {
|
|
return html`
|
|
<div class="org-list">
|
|
${this.organizations.map((org) => html`
|
|
<div class="org-item" @click=${() => this.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>
|
|
`;
|
|
}
|
|
|
|
private renderEmptyState(): TemplateResult {
|
|
return html`
|
|
<div class="empty-state">
|
|
<dees-icon .icon=${'lucide:building2'}></dees-icon>
|
|
<p>You don't have any organizations yet.</p>
|
|
<dees-button @clicked=${this.handleCreateOrg}>
|
|
<dees-icon .icon=${'lucide:plus'} slot="iconLeft"></dees-icon>
|
|
Create Organization
|
|
</dees-button>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
public show(options: {
|
|
targetPath: string;
|
|
title?: string;
|
|
description?: string;
|
|
}) {
|
|
this.targetPath = options.targetPath;
|
|
this.title = options.title || 'Select Organization';
|
|
this.description = options.description || 'Choose an organization to continue.';
|
|
|
|
// Load organizations from state
|
|
const state = accountStateModule.accountState.getState();
|
|
this.organizations = state.organizations;
|
|
|
|
this.visible = true;
|
|
this.setAttribute('visible', '');
|
|
}
|
|
|
|
public hide() {
|
|
this.visible = false;
|
|
this.removeAttribute('visible');
|
|
}
|
|
|
|
private handleOverlayClick(e: Event) {
|
|
if ((e.target as HTMLElement).classList.contains('overlay')) {
|
|
this.hide();
|
|
}
|
|
}
|
|
|
|
private handleCancel() {
|
|
this.hide();
|
|
}
|
|
|
|
private handleSelectOrg(org: plugins.idpInterfaces.data.IOrganization) {
|
|
accountStateModule.accountState.dispatchAction(accountStateModule.setSelectedOrg, org);
|
|
|
|
// Replace :orgName placeholder with actual slug
|
|
const path = this.targetPath.replace(':orgName', org.data.slug);
|
|
|
|
this.dispatchEvent(new CustomEvent('org-selected', {
|
|
bubbles: true,
|
|
composed: true,
|
|
detail: { org, path },
|
|
}));
|
|
|
|
this.hide();
|
|
}
|
|
|
|
private handleCreateOrg() {
|
|
this.hide();
|
|
this.dispatchEvent(new CustomEvent('open-create-org-modal', {
|
|
bubbles: true,
|
|
composed: true,
|
|
}));
|
|
}
|
|
}
|