2026-03-20 16:43:44 +00:00
|
|
|
import * as appstate from '../appstate.js';
|
|
|
|
|
import * as shared from './shared/index.js';
|
2026-03-20 16:48:04 +00:00
|
|
|
import { appRouter } from '../router.js';
|
2026-03-20 16:43:44 +00:00
|
|
|
import {
|
|
|
|
|
css,
|
|
|
|
|
cssManager,
|
|
|
|
|
customElement,
|
|
|
|
|
DeesElement,
|
|
|
|
|
html,
|
|
|
|
|
state,
|
|
|
|
|
type TemplateResult,
|
|
|
|
|
} from '@design.estate/dees-element';
|
|
|
|
|
|
|
|
|
|
@customElement('sg-view-organizations')
|
|
|
|
|
export class SgViewOrganizations extends DeesElement {
|
|
|
|
|
@state()
|
|
|
|
|
accessor organizationsState: appstate.IOrganizationsState = {
|
|
|
|
|
organizations: [],
|
|
|
|
|
currentOrg: null,
|
|
|
|
|
repositories: [],
|
|
|
|
|
members: [],
|
2026-03-21 10:54:10 +00:00
|
|
|
redirects: [],
|
2026-03-20 16:43:44 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
@state()
|
|
|
|
|
accessor uiState: appstate.IUiState = { activeView: 'organizations' };
|
|
|
|
|
|
2026-03-20 16:48:04 +00:00
|
|
|
@state()
|
|
|
|
|
accessor detailOrgId: string | null = null;
|
|
|
|
|
|
2026-03-20 16:43:44 +00:00
|
|
|
constructor() {
|
|
|
|
|
super();
|
|
|
|
|
const orgSub = appstate.organizationsStatePart
|
|
|
|
|
.select((s) => s)
|
|
|
|
|
.subscribe((s) => {
|
|
|
|
|
this.organizationsState = s;
|
|
|
|
|
});
|
|
|
|
|
this.rxSubscriptions.push(orgSub);
|
|
|
|
|
|
|
|
|
|
const uiSub = appstate.uiStatePart
|
|
|
|
|
.select((s) => s)
|
|
|
|
|
.subscribe((s) => {
|
|
|
|
|
this.uiState = s;
|
|
|
|
|
});
|
|
|
|
|
this.rxSubscriptions.push(uiSub);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static styles = [
|
|
|
|
|
cssManager.defaultStyles,
|
|
|
|
|
shared.viewHostCss,
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
async connectedCallback() {
|
|
|
|
|
super.connectedCallback();
|
|
|
|
|
await appstate.organizationsStatePart.dispatchAction(appstate.fetchOrganizationsAction, null);
|
2026-03-20 16:48:04 +00:00
|
|
|
// If there's an entity ID from the URL, copy it to internal state
|
2026-03-20 16:43:44 +00:00
|
|
|
if (this.uiState.activeEntityId) {
|
2026-03-20 16:48:04 +00:00
|
|
|
this.detailOrgId = this.uiState.activeEntityId;
|
|
|
|
|
await this.loadOrgDetail(this.detailOrgId);
|
2026-03-20 16:43:44 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async loadOrgDetail(orgId: string) {
|
|
|
|
|
await appstate.organizationsStatePart.dispatchAction(
|
|
|
|
|
appstate.fetchOrganizationAction,
|
|
|
|
|
{ organizationId: orgId },
|
|
|
|
|
);
|
|
|
|
|
await appstate.organizationsStatePart.dispatchAction(
|
|
|
|
|
appstate.fetchRepositoriesAction,
|
|
|
|
|
{ organizationId: orgId },
|
|
|
|
|
);
|
|
|
|
|
await appstate.organizationsStatePart.dispatchAction(
|
|
|
|
|
appstate.fetchMembersAction,
|
|
|
|
|
{ organizationId: orgId },
|
|
|
|
|
);
|
2026-03-21 10:54:10 +00:00
|
|
|
await appstate.organizationsStatePart.dispatchAction(
|
|
|
|
|
appstate.fetchRedirectsAction,
|
|
|
|
|
{ organizationId: orgId },
|
|
|
|
|
);
|
2026-03-20 16:43:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public render(): TemplateResult {
|
2026-03-20 16:48:04 +00:00
|
|
|
if (this.detailOrgId && this.organizationsState.currentOrg) {
|
2026-03-20 16:43:44 +00:00
|
|
|
return html`
|
|
|
|
|
<sg-organization-detail-view
|
|
|
|
|
.organization="${this.organizationsState.currentOrg}"
|
|
|
|
|
.repositories="${this.organizationsState.repositories}"
|
|
|
|
|
.members="${this.organizationsState.members}"
|
2026-03-21 10:54:10 +00:00
|
|
|
.redirects="${this.organizationsState.redirects}"
|
2026-03-20 16:43:44 +00:00
|
|
|
@back="${() => this.goBack()}"
|
|
|
|
|
@select-repo="${(e: CustomEvent) => this.selectRepo(e.detail.repositoryId)}"
|
|
|
|
|
@create-repo="${() => {/* TODO: create repo modal */}}"
|
2026-03-20 16:48:04 +00:00
|
|
|
@edit="${(e: CustomEvent) => this.handleEditOrg(e.detail)}"
|
|
|
|
|
@delete="${(e: CustomEvent) => this.handleDeleteOrg(e.detail.organizationId)}"
|
2026-03-21 10:54:10 +00:00
|
|
|
@delete-redirect="${(e: CustomEvent) => this.handleDeleteRedirect(e.detail.redirectId)}"
|
2026-03-20 16:43:44 +00:00
|
|
|
></sg-organization-detail-view>
|
|
|
|
|
`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return html`
|
|
|
|
|
<sg-organizations-list-view
|
|
|
|
|
.organizations="${this.organizationsState.organizations}"
|
|
|
|
|
@select="${(e: CustomEvent) => this.selectOrg(e.detail.organizationId)}"
|
|
|
|
|
@create="${(e: CustomEvent) => this.createOrg(e.detail)}"
|
|
|
|
|
></sg-organizations-list-view>
|
|
|
|
|
`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private selectOrg(orgId: string) {
|
2026-03-20 16:48:04 +00:00
|
|
|
this.detailOrgId = orgId;
|
2026-03-20 16:43:44 +00:00
|
|
|
this.loadOrgDetail(orgId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private selectRepo(repoId: string) {
|
|
|
|
|
// Navigate to repository within org context
|
|
|
|
|
// For now, we could switch to packages view
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private goBack() {
|
2026-03-20 16:48:04 +00:00
|
|
|
this.detailOrgId = null;
|
2026-03-20 16:43:44 +00:00
|
|
|
appstate.organizationsStatePart.setState({
|
|
|
|
|
...appstate.organizationsStatePart.getState(),
|
|
|
|
|
currentOrg: null,
|
|
|
|
|
repositories: [],
|
|
|
|
|
members: [],
|
2026-03-21 10:54:10 +00:00
|
|
|
redirects: [],
|
2026-03-20 16:43:44 +00:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-21 10:54:10 +00:00
|
|
|
private async handleDeleteRedirect(redirectId: string) {
|
|
|
|
|
if (!this.detailOrgId) return;
|
|
|
|
|
await appstate.organizationsStatePart.dispatchAction(
|
|
|
|
|
appstate.deleteRedirectAction,
|
|
|
|
|
{ redirectId, organizationId: this.detailOrgId },
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-20 16:48:04 +00:00
|
|
|
private async handleEditOrg(data: {
|
|
|
|
|
organizationId: string;
|
2026-03-21 10:54:10 +00:00
|
|
|
name?: string;
|
2026-03-20 16:48:04 +00:00
|
|
|
displayName?: string;
|
|
|
|
|
description?: string;
|
|
|
|
|
website?: string;
|
|
|
|
|
isPublic?: boolean;
|
|
|
|
|
}) {
|
|
|
|
|
await appstate.organizationsStatePart.dispatchAction(
|
|
|
|
|
appstate.updateOrganizationAction,
|
|
|
|
|
data,
|
|
|
|
|
);
|
|
|
|
|
// Re-load detail to reflect changes
|
|
|
|
|
if (this.detailOrgId) {
|
|
|
|
|
await this.loadOrgDetail(this.detailOrgId);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async handleDeleteOrg(organizationId: string) {
|
|
|
|
|
await appstate.organizationsStatePart.dispatchAction(
|
|
|
|
|
appstate.deleteOrganizationAction,
|
|
|
|
|
{ organizationId },
|
|
|
|
|
);
|
|
|
|
|
this.goBack();
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-20 16:43:44 +00:00
|
|
|
private async createOrg(data: { name: string; displayName?: string; description?: string }) {
|
|
|
|
|
await appstate.organizationsStatePart.dispatchAction(
|
|
|
|
|
appstate.createOrganizationAction,
|
|
|
|
|
data,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|