Skip to content

Commit

Permalink
Initial work on controlled list manager archesproject#10541
Browse files Browse the repository at this point in the history
  • Loading branch information
jacobtylerwalls committed Jan 19, 2024
1 parent 90df509 commit 526128f
Show file tree
Hide file tree
Showing 13 changed files with 788 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import ko from 'knockout';
import { createApp } from 'vue';
import PrimeVue from 'primevue/config';
import ControlledListManagerApp from '@/App.vue';
import ControlledListManager from 'templates/views/components/plugins/controlled-list-manager.htm';

import 'primevue/resources/themes/bootstrap4-light-blue/theme.css';

ko.components.register('controlled-list-manager', {
viewModel: function() {
const app = createApp(ControlledListManagerApp);
app.use(PrimeVue);
app.mount('#controlled-list-mounting-point');
},
template: ControlledListManager,
});
108 changes: 108 additions & 0 deletions arches/app/models/migrations/10541_controlled_list_manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# Generated by Django 4.2.9 on 2024-01-19 12:11

import uuid

import django.db.models.deletion
from django.db import migrations, models
from django.utils.translation import gettext as _


def add_plugins(apps, schema_editor):
Plugin = apps.get_model("models", "Plugin")

Plugin.objects.update_or_create(
pluginid="29321ce0-bd95-4357-a2a5-822e9cb06f70",
name=_("Controlled List Manager"),
icon="fa fa-code-fork",
component="views/components/plugins/controlled-list-manager",
componentname="controlled-list-manager",
config={},
slug="controlled-list-manager",
sortorder=0,
)


def remove_plugin(apps, schema_editor):
Plugin = apps.get_model("models", "Plugin")

Plugin.objects.filter(slug="controlled-list-manager").delete()


class Migration(migrations.Migration):
dependencies = [
("models", "10515_i18n_plugins"),
]

operations = [
migrations.RunPython(add_plugins, remove_plugin),
migrations.CreateModel(
name="ControlledList",
fields=[
("id", models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
("name", models.CharField(max_length=127)),
("dynamic", models.BooleanField(default=False)),
],
options={
"db_table": "controlled_lists",
},
),
migrations.CreateModel(
name="ControlledListItem",
fields=[
("id", models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
("uri", models.URLField(max_length=2048, null=True, unique=True)),
(
"list",
models.ForeignKey(
db_column="listid", on_delete=django.db.models.deletion.CASCADE, related_name="items", to="models.controlledlist"
),
),
(
"parent",
models.ForeignKey(
null=True, on_delete=django.db.models.deletion.CASCADE, related_name="children", to="models.controlledlistitem"
),
),
],
options={
"db_table": "controlled_list_items",
},
),
migrations.CreateModel(
name="Label",
fields=[
("id", models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
("value", models.CharField(max_length=1024)),
(
"item",
models.ForeignKey(
db_column="itemid",
on_delete=django.db.models.deletion.CASCADE,
related_name="labels",
to="models.controlledlistitem",
),
),
(
"language",
models.ForeignKey(
db_column="languageid", on_delete=django.db.models.deletion.PROTECT, to="models.language", to_field="code"
),
),
(
"value_type",
models.ForeignKey(
limit_choices_to={"category": "label"}, on_delete=django.db.models.deletion.PROTECT, to="models.dvaluetype"
),
),
],
options={
"db_table": "controlled_list_labels",
},
),
migrations.AddConstraint(
model_name="label",
constraint=models.UniqueConstraint(
fields=("item", "value", "value_type", "language"), name="unique_item_value_valuetype_language"
),
),
]
57 changes: 57 additions & 0 deletions arches/app/models/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1835,3 +1835,60 @@ def __str__(self):
class Meta:
managed = True
db_table = "spatial_views"


### Controlled List Manager
class ControlledList(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
name = models.CharField(max_length=127, null=False)
dynamic = models.BooleanField(default=False)

class Meta:
db_table = "controlled_lists"


class ControlledListItem(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
# TODO(?): expose schemes
uri = models.URLField(max_length=2048, null=True, unique=True)
list = models.ForeignKey(
ControlledList,
db_column="listid",
on_delete=models.CASCADE,
related_name="items",
)
parent = models.ForeignKey(
"self", null=True, on_delete=models.CASCADE, related_name="children"
)

class Meta:
db_table = "controlled_list_items"


class Label(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
item = models.ForeignKey(
ControlledListItem,
db_column="itemid",
on_delete=models.CASCADE,
related_name="labels",
)
value_type = models.ForeignKey(
DValueType, on_delete=models.PROTECT, limit_choices_to={"category": "label"}
)
language = models.ForeignKey(
Language,
db_column="languageid",
to_field="code",
on_delete=models.PROTECT,
)
value = models.CharField(max_length=1024, null=False)

class Meta:
db_table = "controlled_list_labels"
constraints = [
models.UniqueConstraint(
fields=["item", "value", "value_type", "language"],
name="unique_item_value_valuetype_language",
)
]
13 changes: 13 additions & 0 deletions arches/app/src/App.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<script setup>
import ControlledListManager from "@/components/ControlledListManager.vue";
</script>

<template>
<ControlledListManager />
</template>

<style>
div {
font-family: "Open Sans";
}
</style>
42 changes: 42 additions & 0 deletions arches/app/src/components/ControlledListManager.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<script setup>
import { ref } from "vue";
import Splitter from "primevue/splitter";
import SplitterPanel from "primevue/splitterpanel";
import ControlledListSidebar from "./ControlledListSidebar.vue";
import ControlledListTable from "./ControlledListTable.vue";
const lightGray = "#f4f4f4";
const selectedList = ref({});
</script>

<template>
<Splitter
:pt="{
gutter: { style: { background: lightGray } },
gutterHandler: { style: { background: lightGray } },
}"
>
<SplitterPanel :size="30" :minSize="15">
<ControlledListSidebar :selectedList="selectedList" />
</SplitterPanel>

<SplitterPanel :size="75" :minSize="50" class="mt-0">
<ControlledListTable :selectedList="selectedList" />
</SplitterPanel>
</Splitter>
</template>

<style scoped>
.p-splitter {
width: calc(100vw - 50px);
height: 100vh;
background: white;
font-size: 14px;
}
.p-splitter-panel {
display: flex;
flex-direction: column;
}
</style>
110 changes: 110 additions & 0 deletions arches/app/src/components/ControlledListSidebar.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<script setup>
import arches from "arches";
import Button from "primevue/button";
import InputText from "primevue/inputtext";
import SplitButton from "primevue/splitbutton";
import ControlledListsAll from "./ControlledListsAll.vue";
import Spinner from "./Spinner.vue";
const buttonGreen = "#10b981";
const buttonPink = "#ed7979";
const { selectedList } = defineProps(["selectedList"]);
</script>

<template>
<div class="header">
<h4>{{ arches.translations.controlledLists }}</h4>
</div>

<div class="controls">
<!-- TODO: use .p-input-icon-right once we have primeicons -->
<!-- until then, leave the search icon stranded outside the box -->
<span class="flex">
<InputText
type="text"
class="control"
:placeholder="arches.translations.find"
/>
<i
class="fa fa-search"
:aria-label="arches.translations.search"
style="align-self: center"
></i>
</span>
<div class="flex" style="flex: 0.8; flex-wrap: wrap">
<!-- // QUESTION: should be able to do passthrough attribs once with just "root"? -->
<SplitButton
class="button"
label="Create New List"
raised
style="font-size: inherit"
:pt="{
button: {
root: {
style: {
background: buttonGreen,
border: `1px solid ${buttonGreen}`,
},
},
},
menubutton: {
root: {
style: {
background: buttonGreen,
border: `1px solid ${buttonGreen}`,
},
},
},
}"
></SplitButton>
<Button class="button delete" label="Delete List" raised></Button>
</div>
</div>

<Suspense>
<ControlledListsAll :selectedList="selectedList" />
<template #fallback>
<Spinner />
</template>
</Suspense>
</template>

<style scoped>
.header {
background: #f4f4f4;
display: flex;
align-items: center;
}
h4 {
margin: 1rem;
}
i {
margin-left: 1rem;
}
.controls {
display: flex;
flex-direction: column;
padding: 1rem;
background: #f3fbfd;
}
.p-inputtext {
flex: 0.95;
margin: 0.5rem;
}
.button {
height: 4rem;
margin: 0.5rem;
flex: 0.5;
justify-content: center;
font-weight: 600;
color: white;
text-wrap: nowrap;
}
.button.delete {
background: v-bind(buttonPink);
border: 1px solid v-bind(buttonPink);
}
</style>
Loading

0 comments on commit 526128f

Please sign in to comment.