Release v0.1.8

This commit is contained in:
2026-07-11 16:49:03 +02:00
parent 213157aca1
commit 39c081c4fb
12 changed files with 237 additions and 46 deletions

View File

@@ -1,5 +1,9 @@
# GovOPlaN Organizations
<!-- govoplan-repository-type:start -->
**Repository type:** module (platform).
<!-- govoplan-repository-type:end -->
`govoplan-organizations` is the canonical organizational model module for
GovOPlaN.

View File

@@ -1,6 +1,6 @@
{
"name": "@govoplan/organizations-webui",
"version": "0.1.7",
"version": "0.1.8",
"private": true,
"type": "module",
"main": "webui/src/index.ts",
@@ -19,7 +19,7 @@
"LICENSE"
],
"peerDependencies": {
"@govoplan/core-webui": "^0.1.7",
"@govoplan/core-webui": "^0.1.8",
"@vitejs/plugin-react": "^4.3.4",
"lucide-react": "^1.23.0",
"react": "^19.0.0",

View File

@@ -4,14 +4,14 @@ build-backend = "setuptools.build_meta"
[project]
name = "govoplan-organizations"
version = "0.1.7"
version = "0.1.8"
description = "GovOPlaN organizational model module."
readme = "README.md"
requires-python = ">=3.12"
authors = [{ name = "GovOPlaN" }]
dependencies = [
"govoplan-core>=0.1.7",
"govoplan-tenancy>=0.1.7",
"govoplan-core>=0.1.8",
"govoplan-tenancy>=0.1.8",
]
[tool.setuptools.packages.find]

View File

@@ -86,7 +86,7 @@ def _organization_directory(context: ModuleContext) -> object:
manifest = ModuleManifest(
id="organizations",
name="Organizations",
version="0.1.7",
version="0.1.8",
required_capabilities=(CAPABILITY_AUTH_PRINCIPAL_RESOLVER, CAPABILITY_AUTH_PERMISSION_EVALUATOR),
optional_dependencies=("tenancy", "access", "audit", "policy"),
permissions=PERMISSIONS,

View File

@@ -0,0 +1 @@
"""Organizations Alembic revisions."""

View File

@@ -0,0 +1,187 @@
"""v0.1.7 organizations baseline
Revision ID: 6d7e8f9a0b1c
Revises: None
Create Date: 2026-07-11 00:00:00.000000
"""
from __future__ import annotations
from alembic import op
import sqlalchemy as sa
revision = '6d7e8f9a0b1c'
down_revision = None
branch_labels = None
depends_on = '4f2a9c8e7b6d'
def upgrade() -> None:
op.create_table('organizations_structures',
sa.Column('id', sa.String(length=36), nullable=False),
sa.Column('tenant_id', sa.String(length=36), nullable=False),
sa.Column('slug', sa.String(length=100), nullable=False),
sa.Column('name', sa.String(length=255), nullable=False),
sa.Column('description', sa.Text(), nullable=True),
sa.Column('structure_kind', sa.String(length=30), nullable=False),
sa.Column('is_active', sa.Boolean(), nullable=False),
sa.Column('settings', sa.JSON(), nullable=False),
sa.Column('created_at', sa.DateTime(timezone=True), nullable=False),
sa.Column('updated_at', sa.DateTime(timezone=True), nullable=False),
sa.PrimaryKeyConstraint('id', name=op.f('pk_organizations_structures')),
sa.UniqueConstraint('tenant_id', 'slug', name='uq_organizations_structures_tenant_slug')
)
op.create_index(op.f('ix_organizations_structures_tenant_id'), 'organizations_structures', ['tenant_id'], unique=False)
op.create_table('organizations_tenant_settings',
sa.Column('id', sa.String(length=36), nullable=False),
sa.Column('tenant_id', sa.String(length=36), nullable=False),
sa.Column('allow_tenant_model_customization', sa.Boolean(), nullable=False),
sa.Column('require_model_change_requests', sa.Boolean(), nullable=False),
sa.Column('audit_detail_level', sa.String(length=30), nullable=False),
sa.Column('change_retention_days', sa.Integer(), nullable=True),
sa.Column('settings', sa.JSON(), nullable=False),
sa.Column('created_at', sa.DateTime(timezone=True), nullable=False),
sa.Column('updated_at', sa.DateTime(timezone=True), nullable=False),
sa.PrimaryKeyConstraint('id', name=op.f('pk_organizations_tenant_settings')),
sa.UniqueConstraint('tenant_id', name='uq_organizations_tenant_settings_tenant')
)
op.create_index(op.f('ix_organizations_tenant_settings_tenant_id'), 'organizations_tenant_settings', ['tenant_id'], unique=False)
op.create_table('organizations_unit_types',
sa.Column('id', sa.String(length=36), nullable=False),
sa.Column('tenant_id', sa.String(length=36), nullable=False),
sa.Column('slug', sa.String(length=100), nullable=False),
sa.Column('name', sa.String(length=255), nullable=False),
sa.Column('description', sa.Text(), nullable=True),
sa.Column('is_active', sa.Boolean(), nullable=False),
sa.Column('settings', sa.JSON(), nullable=False),
sa.Column('created_at', sa.DateTime(timezone=True), nullable=False),
sa.Column('updated_at', sa.DateTime(timezone=True), nullable=False),
sa.PrimaryKeyConstraint('id', name=op.f('pk_organizations_unit_types')),
sa.UniqueConstraint('tenant_id', 'slug', name='uq_organizations_unit_types_tenant_slug')
)
op.create_index(op.f('ix_organizations_unit_types_tenant_id'), 'organizations_unit_types', ['tenant_id'], unique=False)
op.create_table('organizations_function_types',
sa.Column('id', sa.String(length=36), nullable=False),
sa.Column('tenant_id', sa.String(length=36), nullable=False),
sa.Column('slug', sa.String(length=100), nullable=False),
sa.Column('name', sa.String(length=255), nullable=False),
sa.Column('description', sa.Text(), nullable=True),
sa.Column('organization_unit_type_id', sa.String(length=36), nullable=True),
sa.Column('delegable', sa.Boolean(), nullable=False),
sa.Column('act_in_place_allowed', sa.Boolean(), nullable=False),
sa.Column('is_active', sa.Boolean(), nullable=False),
sa.Column('settings', sa.JSON(), nullable=False),
sa.Column('created_at', sa.DateTime(timezone=True), nullable=False),
sa.Column('updated_at', sa.DateTime(timezone=True), nullable=False),
sa.ForeignKeyConstraint(['organization_unit_type_id'], ['organizations_unit_types.id'], name=op.f('fk_organizations_function_types_organization_unit_type_id_organizations_unit_types'), ondelete='SET NULL'),
sa.PrimaryKeyConstraint('id', name=op.f('pk_organizations_function_types')),
sa.UniqueConstraint('tenant_id', 'slug', name='uq_organizations_function_types_tenant_slug')
)
op.create_index(op.f('ix_organizations_function_types_organization_unit_type_id'), 'organizations_function_types', ['organization_unit_type_id'], unique=False)
op.create_index(op.f('ix_organizations_function_types_tenant_id'), 'organizations_function_types', ['tenant_id'], unique=False)
op.create_table('organizations_relation_types',
sa.Column('id', sa.String(length=36), nullable=False),
sa.Column('tenant_id', sa.String(length=36), nullable=False),
sa.Column('structure_id', sa.String(length=36), nullable=True),
sa.Column('slug', sa.String(length=100), nullable=False),
sa.Column('name', sa.String(length=255), nullable=False),
sa.Column('description', sa.Text(), nullable=True),
sa.Column('source_unit_type_id', sa.String(length=36), nullable=True),
sa.Column('target_unit_type_id', sa.String(length=36), nullable=True),
sa.Column('is_hierarchical', sa.Boolean(), nullable=False),
sa.Column('allow_cycles', sa.Boolean(), nullable=False),
sa.Column('is_active', sa.Boolean(), nullable=False),
sa.Column('settings', sa.JSON(), nullable=False),
sa.Column('created_at', sa.DateTime(timezone=True), nullable=False),
sa.Column('updated_at', sa.DateTime(timezone=True), nullable=False),
sa.ForeignKeyConstraint(['source_unit_type_id'], ['organizations_unit_types.id'], name=op.f('fk_organizations_relation_types_source_unit_type_id_organizations_unit_types'), ondelete='SET NULL'),
sa.ForeignKeyConstraint(['structure_id'], ['organizations_structures.id'], name=op.f('fk_organizations_relation_types_structure_id_organizations_structures'), ondelete='SET NULL'),
sa.ForeignKeyConstraint(['target_unit_type_id'], ['organizations_unit_types.id'], name=op.f('fk_organizations_relation_types_target_unit_type_id_organizations_unit_types'), ondelete='SET NULL'),
sa.PrimaryKeyConstraint('id', name=op.f('pk_organizations_relation_types')),
sa.UniqueConstraint('tenant_id', 'slug', name='uq_organizations_relation_types_tenant_slug')
)
op.create_index(op.f('ix_organizations_relation_types_source_unit_type_id'), 'organizations_relation_types', ['source_unit_type_id'], unique=False)
op.create_index('ix_organizations_relation_types_structure', 'organizations_relation_types', ['tenant_id', 'structure_id'], unique=False)
op.create_index(op.f('ix_organizations_relation_types_structure_id'), 'organizations_relation_types', ['structure_id'], unique=False)
op.create_index(op.f('ix_organizations_relation_types_target_unit_type_id'), 'organizations_relation_types', ['target_unit_type_id'], unique=False)
op.create_index(op.f('ix_organizations_relation_types_tenant_id'), 'organizations_relation_types', ['tenant_id'], unique=False)
op.create_table('organizations_units',
sa.Column('id', sa.String(length=36), nullable=False),
sa.Column('tenant_id', sa.String(length=36), nullable=False),
sa.Column('unit_type_id', sa.String(length=36), nullable=True),
sa.Column('parent_id', sa.String(length=36), nullable=True),
sa.Column('slug', sa.String(length=100), nullable=False),
sa.Column('name', sa.String(length=255), nullable=False),
sa.Column('description', sa.Text(), nullable=True),
sa.Column('is_active', sa.Boolean(), nullable=False),
sa.Column('settings', sa.JSON(), nullable=False),
sa.Column('created_at', sa.DateTime(timezone=True), nullable=False),
sa.Column('updated_at', sa.DateTime(timezone=True), nullable=False),
sa.ForeignKeyConstraint(['parent_id'], ['organizations_units.id'], name=op.f('fk_organizations_units_parent_id_organizations_units'), ondelete='SET NULL'),
sa.ForeignKeyConstraint(['unit_type_id'], ['organizations_unit_types.id'], name=op.f('fk_organizations_units_unit_type_id_organizations_unit_types'), ondelete='SET NULL'),
sa.PrimaryKeyConstraint('id', name=op.f('pk_organizations_units')),
sa.UniqueConstraint('tenant_id', 'slug', name='uq_organizations_units_tenant_slug')
)
op.create_index(op.f('ix_organizations_units_parent_id'), 'organizations_units', ['parent_id'], unique=False)
op.create_index(op.f('ix_organizations_units_tenant_id'), 'organizations_units', ['tenant_id'], unique=False)
op.create_index(op.f('ix_organizations_units_unit_type_id'), 'organizations_units', ['unit_type_id'], unique=False)
op.create_table('organizations_functions',
sa.Column('id', sa.String(length=36), nullable=False),
sa.Column('tenant_id', sa.String(length=36), nullable=False),
sa.Column('function_type_id', sa.String(length=36), nullable=True),
sa.Column('organization_unit_id', sa.String(length=36), nullable=False),
sa.Column('slug', sa.String(length=100), nullable=False),
sa.Column('name', sa.String(length=255), nullable=False),
sa.Column('description', sa.Text(), nullable=True),
sa.Column('delegable', sa.Boolean(), nullable=False),
sa.Column('act_in_place_allowed', sa.Boolean(), nullable=False),
sa.Column('is_active', sa.Boolean(), nullable=False),
sa.Column('settings', sa.JSON(), nullable=False),
sa.Column('created_at', sa.DateTime(timezone=True), nullable=False),
sa.Column('updated_at', sa.DateTime(timezone=True), nullable=False),
sa.ForeignKeyConstraint(['function_type_id'], ['organizations_function_types.id'], name=op.f('fk_organizations_functions_function_type_id_organizations_function_types'), ondelete='SET NULL'),
sa.ForeignKeyConstraint(['organization_unit_id'], ['organizations_units.id'], name=op.f('fk_organizations_functions_organization_unit_id_organizations_units'), ondelete='CASCADE'),
sa.PrimaryKeyConstraint('id', name=op.f('pk_organizations_functions')),
sa.UniqueConstraint('tenant_id', 'organization_unit_id', 'slug', name='uq_organizations_functions_tenant_unit_slug')
)
op.create_index(op.f('ix_organizations_functions_function_type_id'), 'organizations_functions', ['function_type_id'], unique=False)
op.create_index(op.f('ix_organizations_functions_organization_unit_id'), 'organizations_functions', ['organization_unit_id'], unique=False)
op.create_index(op.f('ix_organizations_functions_tenant_id'), 'organizations_functions', ['tenant_id'], unique=False)
op.create_table('organizations_relations',
sa.Column('id', sa.String(length=36), nullable=False),
sa.Column('tenant_id', sa.String(length=36), nullable=False),
sa.Column('structure_id', sa.String(length=36), nullable=False),
sa.Column('relation_type_id', sa.String(length=36), nullable=False),
sa.Column('source_unit_id', sa.String(length=36), nullable=False),
sa.Column('target_unit_id', sa.String(length=36), nullable=False),
sa.Column('valid_from', sa.DateTime(timezone=True), nullable=True),
sa.Column('valid_until', sa.DateTime(timezone=True), nullable=True),
sa.Column('is_active', sa.Boolean(), nullable=False),
sa.Column('settings', sa.JSON(), nullable=False),
sa.Column('created_at', sa.DateTime(timezone=True), nullable=False),
sa.Column('updated_at', sa.DateTime(timezone=True), nullable=False),
sa.ForeignKeyConstraint(['relation_type_id'], ['organizations_relation_types.id'], name=op.f('fk_organizations_relations_relation_type_id_organizations_relation_types'), ondelete='CASCADE'),
sa.ForeignKeyConstraint(['source_unit_id'], ['organizations_units.id'], name=op.f('fk_organizations_relations_source_unit_id_organizations_units'), ondelete='CASCADE'),
sa.ForeignKeyConstraint(['structure_id'], ['organizations_structures.id'], name=op.f('fk_organizations_relations_structure_id_organizations_structures'), ondelete='CASCADE'),
sa.ForeignKeyConstraint(['target_unit_id'], ['organizations_units.id'], name=op.f('fk_organizations_relations_target_unit_id_organizations_units'), ondelete='CASCADE'),
sa.PrimaryKeyConstraint('id', name=op.f('pk_organizations_relations')),
sa.UniqueConstraint('tenant_id', 'structure_id', 'relation_type_id', 'source_unit_id', 'target_unit_id', name='uq_organizations_relations_edge')
)
op.create_index(op.f('ix_organizations_relations_relation_type_id'), 'organizations_relations', ['relation_type_id'], unique=False)
op.create_index(op.f('ix_organizations_relations_source_unit_id'), 'organizations_relations', ['source_unit_id'], unique=False)
op.create_index(op.f('ix_organizations_relations_structure_id'), 'organizations_relations', ['structure_id'], unique=False)
op.create_index('ix_organizations_relations_structure_source', 'organizations_relations', ['tenant_id', 'structure_id', 'source_unit_id'], unique=False)
op.create_index('ix_organizations_relations_structure_target', 'organizations_relations', ['tenant_id', 'structure_id', 'target_unit_id'], unique=False)
op.create_index(op.f('ix_organizations_relations_target_unit_id'), 'organizations_relations', ['target_unit_id'], unique=False)
op.create_index(op.f('ix_organizations_relations_tenant_id'), 'organizations_relations', ['tenant_id'], unique=False)
def downgrade() -> None:
op.drop_table('organizations_relations')
op.drop_table('organizations_functions')
op.drop_table('organizations_units')
op.drop_table('organizations_relation_types')
op.drop_table('organizations_function_types')
op.drop_table('organizations_unit_types')
op.drop_table('organizations_tenant_settings')
op.drop_table('organizations_structures')

View File

@@ -1,6 +1,6 @@
{
"name": "@govoplan/organizations-webui",
"version": "0.1.7",
"version": "0.1.8",
"private": true,
"type": "module",
"main": "src/index.ts",
@@ -14,7 +14,7 @@
"./styles/organizations.css": "./src/styles/organizations.css"
},
"peerDependencies": {
"@govoplan/core-webui": "^0.1.7",
"@govoplan/core-webui": "^0.1.8",
"@vitejs/plugin-react": "^4.3.4",
"lucide-react": "^1.23.0",
"react": "^19.0.0",

View File

@@ -13,6 +13,7 @@ import {
ModuleSubnav,
PageTitle,
StatusBadge,
ToggleSwitch,
hasScope,
useUnsavedDraftGuard,
usePlatformUiCapabilities,
@@ -324,10 +325,14 @@ function SluggedFields({
<textarea rows={3} value={draft.description} disabled={disabled} onChange={(event) => onChange({ ...draft, description: event.target.value })} />
</FormField>
</div>
<label className="organizations-inline-check wide">
<input type="checkbox" checked={draft.is_active} disabled={disabled} onChange={(event) => onChange({ ...draft, is_active: event.target.checked })} />
<span>i18n:govoplan-organizations.active.a733b809</span>
</label>
<div className="wide">
<ToggleSwitch
checked={draft.is_active}
disabled={disabled}
onChange={(checked) => onChange({ ...draft, is_active: checked })}
label="i18n:govoplan-organizations.active.a733b809"
/>
</div>
</>
);
}
@@ -982,16 +987,16 @@ export default function OrganizationsPage({
function renderModelSection() {
return (
<div className="organizations-table-stack">
<Card title="i18n:govoplan-organizations.unit_types.c7afc174" collapsible collapseKey="organizations.unit-types" actions={<AdminIconButton label="i18n:govoplan-organizations.add_unit_type.58f2c05a" icon={<Plus size={16} aria-hidden="true" />} variant="primary" disabled={!canWriteModel || busy} onClick={openUnitTypeCreate} />}>
<Card title="i18n:govoplan-organizations.unit_types.c7afc174" actions={<AdminIconButton label="i18n:govoplan-organizations.add_unit_type.58f2c05a" icon={<Plus size={16} aria-hidden="true" />} variant="primary" disabled={!canWriteModel || busy} onClick={openUnitTypeCreate} />}>
<DataGrid id="organizations-unit-types" rows={model.unit_types} columns={unitTypeColumns} getRowKey={(row) => row.id} emptyText="i18n:govoplan-organizations.no_unit_types_found.c81fb2a7" initialFit="container" />
</Card>
<Card title="i18n:govoplan-organizations.structures.f9b7f3b4" collapsible collapseKey="organizations.structures" actions={<AdminIconButton label="i18n:govoplan-organizations.add_structure.b722042a" icon={<Plus size={16} aria-hidden="true" />} variant="primary" disabled={!canWriteModel || busy} onClick={openStructureCreate} />}>
<Card title="i18n:govoplan-organizations.structures.f9b7f3b4" actions={<AdminIconButton label="i18n:govoplan-organizations.add_structure.b722042a" icon={<Plus size={16} aria-hidden="true" />} variant="primary" disabled={!canWriteModel || busy} onClick={openStructureCreate} />}>
<DataGrid id="organizations-structures" rows={model.structures} columns={structureColumns} getRowKey={(row) => row.id} emptyText="i18n:govoplan-organizations.no_structures_found.20b382d0" initialFit="container" />
</Card>
<Card title="i18n:govoplan-organizations.relation_types.e5890528" collapsible collapseKey="organizations.relation-types" actions={<AdminIconButton label="i18n:govoplan-organizations.add_relation_type.2ad19d03" icon={<Plus size={16} aria-hidden="true" />} variant="primary" disabled={!canWriteModel || busy} onClick={openRelationTypeCreate} />}>
<Card title="i18n:govoplan-organizations.relation_types.e5890528" actions={<AdminIconButton label="i18n:govoplan-organizations.add_relation_type.2ad19d03" icon={<Plus size={16} aria-hidden="true" />} variant="primary" disabled={!canWriteModel || busy} onClick={openRelationTypeCreate} />}>
<DataGrid id="organizations-relation-types" rows={model.relation_types} columns={relationTypeColumns} getRowKey={(row) => row.id} emptyText="i18n:govoplan-organizations.no_relation_types_found.6f90bb81" initialFit="container" />
</Card>
<Card title="i18n:govoplan-organizations.function_types.172c01fe" collapsible collapseKey="organizations.function-types" actions={<AdminIconButton label="i18n:govoplan-organizations.add_function_type.90d793f1" icon={<Plus size={16} aria-hidden="true" />} variant="primary" disabled={!canWriteModel || busy} onClick={openFunctionTypeCreate} />}>
<Card title="i18n:govoplan-organizations.function_types.172c01fe" actions={<AdminIconButton label="i18n:govoplan-organizations.add_function_type.90d793f1" icon={<Plus size={16} aria-hidden="true" />} variant="primary" disabled={!canWriteModel || busy} onClick={openFunctionTypeCreate} />}>
<DataGrid id="organizations-function-types" rows={model.function_types} columns={functionTypeColumns} getRowKey={(row) => row.id} emptyText="i18n:govoplan-organizations.no_function_types_found.5eef31b1" initialFit="container" />
</Card>
</div>
@@ -1026,7 +1031,7 @@ export default function OrganizationsPage({
function renderUnitsSection() {
return (
<div className="organizations-table-stack">
<Card title="i18n:govoplan-organizations.organization_tree.e5bfb195" collapsible collapseKey="organizations.tree" actions={<AdminIconButton label="i18n:govoplan-organizations.add_root_unit.1ef8a9f5" icon={<Plus size={16} aria-hidden="true" />} variant="primary" disabled={!canWriteUnits || busy} onClick={() => openUnitCreate()} />}>
<Card title="i18n:govoplan-organizations.organization_tree.e5bfb195" actions={<AdminIconButton label="i18n:govoplan-organizations.add_root_unit.1ef8a9f5" icon={<Plus size={16} aria-hidden="true" />} variant="primary" disabled={!canWriteUnits || busy} onClick={() => openUnitCreate()} />}>
<div className="organizations-tree-toolbar">
{selectedUnitId && <Button type="button" variant="ghost" disabled={busy} onClick={() => setSelectedUnitId("")}>i18n:govoplan-organizations.none.334c4a4c</Button>}
</div>
@@ -1034,7 +1039,7 @@ export default function OrganizationsPage({
{model.units.length ? renderUnitTreeNodes() : <p className="muted small-note">i18n:govoplan-organizations.no_units_found.eea2dd0c</p>}
</div>
</Card>
<Card title="i18n:govoplan-organizations.units.e14d0d92" collapsible collapseKey="organizations.units" actions={<AdminIconButton label="i18n:govoplan-organizations.add_unit.8fa12fb1" icon={<Plus size={16} aria-hidden="true" />} variant="primary" disabled={!canWriteUnits || busy} onClick={() => openUnitCreate()} />}>
<Card title="i18n:govoplan-organizations.units.e14d0d92" actions={<AdminIconButton label="i18n:govoplan-organizations.add_unit.8fa12fb1" icon={<Plus size={16} aria-hidden="true" />} variant="primary" disabled={!canWriteUnits || busy} onClick={() => openUnitCreate()} />}>
<DataGrid id="organizations-units" rows={model.units} columns={unitColumns} getRowKey={(row) => row.id} emptyText="i18n:govoplan-organizations.no_units_found.eea2dd0c" initialFit="container" />
</Card>
</div>
@@ -1044,7 +1049,7 @@ export default function OrganizationsPage({
function renderRelationsSection() {
return (
<div className="organizations-table-stack">
<Card title="i18n:govoplan-organizations.relations.1c796711" collapsible collapseKey="organizations.relations" actions={<AdminIconButton label="i18n:govoplan-organizations.add_relation.6b6e67ea" icon={<Plus size={16} aria-hidden="true" />} variant="primary" disabled={!canWriteUnits || busy} onClick={openRelationCreate} />}>
<Card title="i18n:govoplan-organizations.relations.1c796711" actions={<AdminIconButton label="i18n:govoplan-organizations.add_relation.6b6e67ea" icon={<Plus size={16} aria-hidden="true" />} variant="primary" disabled={!canWriteUnits || busy} onClick={openRelationCreate} />}>
<DataGrid id="organizations-relations" rows={model.relations} columns={relationColumns} getRowKey={(row) => row.id} emptyText="i18n:govoplan-organizations.no_relations_found.4e75b11d" initialFit="container" />
</Card>
</div>
@@ -1054,7 +1059,7 @@ export default function OrganizationsPage({
function renderFunctionsSection() {
return (
<div className="organizations-table-stack">
<Card title="i18n:govoplan-organizations.functions.805dc49b" collapsible collapseKey="organizations.functions" actions={<AdminIconButton label="i18n:govoplan-organizations.add_function.6abafee0" icon={<Plus size={16} aria-hidden="true" />} variant="primary" disabled={!canWriteFunctions || busy} onClick={() => openFunctionCreate()} />}>
<Card title="i18n:govoplan-organizations.functions.805dc49b" actions={<AdminIconButton label="i18n:govoplan-organizations.add_function.6abafee0" icon={<Plus size={16} aria-hidden="true" />} variant="primary" disabled={!canWriteFunctions || busy} onClick={() => openFunctionCreate()} />}>
<DataGrid id="organizations-functions" rows={model.functions} columns={functionColumns} getRowKey={(row) => row.id} emptyText="i18n:govoplan-organizations.no_functions_found.54e759a0" initialFit="container" />
</Card>
</div>
@@ -1176,8 +1181,8 @@ export default function OrganizationsPage({
</select>
</FormField>
<div className="organizations-check-list">
<label><input type="checkbox" checked={relationTypeDraft.is_hierarchical} disabled={!canWriteModel || busy} onChange={(event) => setRelationTypeDraft({ ...relationTypeDraft, is_hierarchical: event.target.checked })} /> i18n:govoplan-organizations.hierarchical.8964f313</label>
<label><input type="checkbox" checked={relationTypeDraft.allow_cycles} disabled={!canWriteModel || busy} onChange={(event) => setRelationTypeDraft({ ...relationTypeDraft, allow_cycles: event.target.checked })} /> i18n:govoplan-organizations.allow_cycles.31327578</label>
<ToggleSwitch checked={relationTypeDraft.is_hierarchical} disabled={!canWriteModel || busy} onChange={(checked) => setRelationTypeDraft({ ...relationTypeDraft, is_hierarchical: checked })} label="i18n:govoplan-organizations.hierarchical.8964f313" />
<ToggleSwitch checked={relationTypeDraft.allow_cycles} disabled={!canWriteModel || busy} onChange={(checked) => setRelationTypeDraft({ ...relationTypeDraft, allow_cycles: checked })} label="i18n:govoplan-organizations.allow_cycles.31327578" />
</div>
{renderChangeRequestField()}
</>
@@ -1235,10 +1240,14 @@ export default function OrganizationsPage({
{model.units.map((item) => <option key={item.id} value={item.id}>{item.name}</option>)}
</select>
</FormField>
<label className="organizations-inline-check wide">
<input type="checkbox" checked={relationDraft.is_active} disabled={!canWriteUnits || busy} onChange={(event) => setRelationDraft({ ...relationDraft, is_active: event.target.checked })} />
<span>i18n:govoplan-organizations.active.a733b809</span>
</label>
<div className="wide">
<ToggleSwitch
checked={relationDraft.is_active}
disabled={!canWriteUnits || busy}
onChange={(checked) => setRelationDraft({ ...relationDraft, is_active: checked })}
label="i18n:govoplan-organizations.active.a733b809"
/>
</div>
{renderChangeRequestField()}
</>
);
@@ -1254,8 +1263,8 @@ export default function OrganizationsPage({
</select>
</FormField>
<div className="organizations-check-list">
<label><input type="checkbox" checked={functionTypeDraft.delegable} disabled={!canWriteModel || busy} onChange={(event) => setFunctionTypeDraft({ ...functionTypeDraft, delegable: event.target.checked })} /> i18n:govoplan-organizations.delegable.b4f0137d</label>
<label><input type="checkbox" checked={functionTypeDraft.act_in_place_allowed} disabled={!canWriteModel || busy} onChange={(event) => setFunctionTypeDraft({ ...functionTypeDraft, act_in_place_allowed: event.target.checked })} /> i18n:govoplan-organizations.act_in_place.49b942bd</label>
<ToggleSwitch checked={functionTypeDraft.delegable} disabled={!canWriteModel || busy} onChange={(checked) => setFunctionTypeDraft({ ...functionTypeDraft, delegable: checked })} label="i18n:govoplan-organizations.delegable.b4f0137d" />
<ToggleSwitch checked={functionTypeDraft.act_in_place_allowed} disabled={!canWriteModel || busy} onChange={(checked) => setFunctionTypeDraft({ ...functionTypeDraft, act_in_place_allowed: checked })} label="i18n:govoplan-organizations.act_in_place.49b942bd" />
</div>
{renderChangeRequestField()}
</>
@@ -1277,8 +1286,8 @@ export default function OrganizationsPage({
</select>
</FormField>
<div className="organizations-check-list">
<label><input type="checkbox" checked={functionDraft.delegable} disabled={!canWriteFunctions || busy} onChange={(event) => setFunctionDraft({ ...functionDraft, delegable: event.target.checked })} /> i18n:govoplan-organizations.delegable.b4f0137d</label>
<label><input type="checkbox" checked={functionDraft.act_in_place_allowed} disabled={!canWriteFunctions || busy} onChange={(event) => setFunctionDraft({ ...functionDraft, act_in_place_allowed: event.target.checked })} /> i18n:govoplan-organizations.act_in_place.49b942bd</label>
<ToggleSwitch checked={functionDraft.delegable} disabled={!canWriteFunctions || busy} onChange={(checked) => setFunctionDraft({ ...functionDraft, delegable: checked })} label="i18n:govoplan-organizations.delegable.b4f0137d" />
<ToggleSwitch checked={functionDraft.act_in_place_allowed} disabled={!canWriteFunctions || busy} onChange={(checked) => setFunctionDraft({ ...functionDraft, act_in_place_allowed: checked })} label="i18n:govoplan-organizations.act_in_place.49b942bd" />
</div>
{renderChangeRequestField()}
</>

View File

@@ -67,9 +67,9 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-organizations.none.334c4a4c": "None",
"i18n:govoplan-organizations.organization_model.4f924c0e": "Organization model",
"i18n:govoplan-organizations.organization_model_admin_description.35dc9f10": "Configure tenant-local unit types, parallel structures, relation types, and function types used by the organization module.",
"i18n:govoplan-organizations.organization_settings.c9ab9829": "Organization settings",
"i18n:govoplan-organizations.organization_settings.c9ab9829": "Organizations",
"i18n:govoplan-organizations.organization_settings_description.35dc9f10": "Configure tenant-level governance, audit, and retention behavior for organization changes.",
"i18n:govoplan-organizations.organization_settings_saved.bfbcfdfa": "Organization settings saved.",
"i18n:govoplan-organizations.organization_settings_saved.bfbcfdfa": "Organizations saved.",
"i18n:govoplan-organizations.organization_tree.e5bfb195": "Organization tree",
"i18n:govoplan-organizations.organizations.220edf64": "Organizations",
"i18n:govoplan-organizations.organizations_intro.4e67c4bb": "Model organization types, concrete units, structures, and functions.",
@@ -190,9 +190,9 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-organizations.none.334c4a4c": "Keine",
"i18n:govoplan-organizations.organization_model.4f924c0e": "Organisationsmodell",
"i18n:govoplan-organizations.organization_model_admin_description.35dc9f10": "Konfiguriere mandantenbezogene Einheitstypen, parallele Strukturen, Beziehungstypen und Funktionstypen des Organisationsmoduls.",
"i18n:govoplan-organizations.organization_settings.c9ab9829": "Organisationseinstellungen",
"i18n:govoplan-organizations.organization_settings.c9ab9829": "Organisationen",
"i18n:govoplan-organizations.organization_settings_description.35dc9f10": "Konfiguriere Governance, Audit und Aufbewahrung für Organisationsänderungen auf Mandantenebene.",
"i18n:govoplan-organizations.organization_settings_saved.bfbcfdfa": "Organisationseinstellungen gespeichert.",
"i18n:govoplan-organizations.organization_settings_saved.bfbcfdfa": "Organisationen gespeichert.",
"i18n:govoplan-organizations.organization_tree.e5bfb195": "Organisationsbaum",
"i18n:govoplan-organizations.organizations.220edf64": "Organisationen",
"i18n:govoplan-organizations.organizations_intro.4e67c4bb": "Modelliere Organisationstypen, konkrete Einheiten, Strukturen und Funktionen.",

View File

@@ -23,7 +23,7 @@ const organizationAdminSections: AdminSectionsUiCapability = {
sections: [
{
id: "tenant-organization-settings",
label: "i18n:govoplan-organizations.organization_settings.c9ab9829",
label: "i18n:govoplan-organizations.organizations.220edf64",
group: "TENANT",
order: 85,
anyOf: ["organizations:settings:read", "admin:settings:read"],
@@ -49,7 +49,7 @@ export const organizationsModule: PlatformWebModule = {
{
to: "/organizations",
label: "i18n:govoplan-organizations.organizations.220edf64",
iconName: "users",
iconName: "building-2",
anyOf: organizationReadScopes,
order: 70
}

View File

@@ -7,7 +7,6 @@
display: grid;
gap: 18px;
width: 100%;
max-width: 1480px;
}
.organizations-admin-page {
@@ -63,15 +62,6 @@
grid-column: 1 / -1;
}
.organizations-check-list label,
.organizations-inline-check {
display: inline-flex;
align-items: center;
gap: 8px;
color: var(--text);
font-weight: 600;
}
.organizations-table-stack {
display: grid;
gap: 18px;