refactored tabs, offers and need

This commit is contained in:
Anton Tranelis 2025-12-16 16:56:38 +01:00
parent e375553b3f
commit 5d3a24fb64
51 changed files with 1667 additions and 59 deletions

View File

@ -0,0 +1,28 @@
{
"collection": "attestations_component",
"meta": {
"accountability": "all",
"archive_app_filter": true,
"archive_field": null,
"archive_value": null,
"collapse": "open",
"collection": "attestations_component",
"color": null,
"display_template": null,
"group": "UI_Components",
"hidden": false,
"icon": "favorite",
"item_duplication_fields": null,
"note": null,
"preview_url": null,
"singleton": false,
"sort": 11,
"sort_field": null,
"translations": null,
"unarchive_value": null,
"versioning": false
},
"schema": {
"name": "attestations_component"
}
}

View File

@ -0,0 +1,28 @@
{
"collection": "tab",
"meta": {
"collection": "tab",
"icon": "tab",
"note": null,
"display_template": "{{title}}",
"hidden": false,
"singleton": false,
"translations": null,
"archive_field": null,
"archive_app_filter": true,
"archive_value": null,
"unarchive_value": null,
"sort_field": "sort",
"accountability": "all",
"color": null,
"item_duplication_fields": null,
"sort": null,
"group": null,
"collapse": "open",
"preview_url": null,
"versioning": false
},
"schema": {
"name": "tab"
}
}

View File

@ -0,0 +1,28 @@
{
"collection": "tab_items",
"meta": {
"collection": "tab_items",
"icon": "import_export",
"note": null,
"display_template": null,
"hidden": true,
"singleton": false,
"translations": null,
"archive_field": null,
"archive_app_filter": true,
"archive_value": null,
"unarchive_value": null,
"sort_field": "sort",
"accountability": "all",
"color": null,
"item_duplication_fields": null,
"sort": null,
"group": null,
"collapse": "open",
"preview_url": null,
"versioning": false
},
"schema": {
"name": "tab_items"
}
}

View File

@ -0,0 +1,28 @@
{
"collection": "tabs",
"meta": {
"accountability": "all",
"archive_app_filter": true,
"archive_field": null,
"archive_value": null,
"collapse": "open",
"collection": "tabs",
"color": null,
"display_template": null,
"group": "UI_Components",
"hidden": false,
"icon": "tab",
"item_duplication_fields": null,
"note": null,
"preview_url": null,
"singleton": false,
"sort": 12,
"sort_field": null,
"translations": null,
"unarchive_value": null,
"versioning": false
},
"schema": {
"name": "tabs"
}
}

View File

@ -0,0 +1,28 @@
{
"collection": "tags_component",
"meta": {
"accountability": "all",
"archive_app_filter": true,
"archive_field": null,
"archive_value": null,
"collapse": "open",
"collection": "tags_component",
"color": null,
"display_template": null,
"group": "UI_Components",
"hidden": false,
"icon": "sell",
"item_duplication_fields": null,
"note": null,
"preview_url": null,
"singleton": false,
"sort": 10,
"sort_field": null,
"translations": null,
"unarchive_value": null,
"versioning": false
},
"schema": {
"name": "tags_component"
}
}

View File

@ -0,0 +1,43 @@
{
"collection": "attestations_component",
"field": "heading",
"type": "string",
"meta": {
"collection": "attestations_component",
"conditions": null,
"display": null,
"display_options": null,
"field": "heading",
"group": null,
"hidden": false,
"interface": "input",
"note": null,
"options": null,
"readonly": false,
"required": false,
"sort": 2,
"special": null,
"translations": null,
"validation": null,
"validation_message": null,
"width": "half"
},
"schema": {
"name": "heading",
"table": "attestations_component",
"data_type": "character varying",
"default_value": "Trust",
"max_length": 255,
"numeric_precision": null,
"numeric_scale": null,
"is_nullable": true,
"is_unique": false,
"is_indexed": false,
"is_primary_key": false,
"is_generated": false,
"generation_expression": null,
"has_auto_increment": false,
"foreign_key_table": null,
"foreign_key_column": null
}
}

View File

@ -0,0 +1,45 @@
{
"collection": "attestations_component",
"field": "hideWhenEmpty",
"type": "boolean",
"meta": {
"collection": "attestations_component",
"conditions": null,
"display": null,
"display_options": null,
"field": "hideWhenEmpty",
"group": null,
"hidden": false,
"interface": "boolean",
"note": null,
"options": null,
"readonly": false,
"required": false,
"sort": 3,
"special": [
"cast-boolean"
],
"translations": null,
"validation": null,
"validation_message": null,
"width": "half"
},
"schema": {
"name": "hideWhenEmpty",
"table": "attestations_component",
"data_type": "boolean",
"default_value": true,
"max_length": null,
"numeric_precision": null,
"numeric_scale": null,
"is_nullable": true,
"is_unique": false,
"is_indexed": false,
"is_primary_key": false,
"is_generated": false,
"generation_expression": null,
"has_auto_increment": false,
"foreign_key_table": null,
"foreign_key_column": null
}
}

View File

@ -0,0 +1,45 @@
{
"collection": "attestations_component",
"field": "id",
"type": "uuid",
"meta": {
"collection": "attestations_component",
"conditions": null,
"display": null,
"display_options": null,
"field": "id",
"group": null,
"hidden": true,
"interface": "input",
"note": null,
"options": null,
"readonly": true,
"required": false,
"sort": 1,
"special": [
"uuid"
],
"translations": null,
"validation": null,
"validation_message": null,
"width": "full"
},
"schema": {
"name": "id",
"table": "attestations_component",
"data_type": "uuid",
"default_value": null,
"max_length": null,
"numeric_precision": null,
"numeric_scale": null,
"is_nullable": false,
"is_unique": true,
"is_indexed": false,
"is_primary_key": true,
"is_generated": false,
"generation_expression": null,
"has_auto_increment": false,
"foreign_key_table": null,
"foreign_key_column": null
}
}

View File

@ -0,0 +1,43 @@
{
"collection": "tab",
"field": "icon",
"type": "string",
"meta": {
"collection": "tab",
"conditions": null,
"display": null,
"display_options": null,
"field": "icon",
"group": null,
"hidden": false,
"interface": "input",
"note": "Optional emoji or icon for the tab",
"options": null,
"readonly": false,
"required": false,
"sort": 3,
"special": null,
"translations": null,
"validation": null,
"validation_message": null,
"width": "half"
},
"schema": {
"name": "icon",
"table": "tab",
"data_type": "character varying",
"default_value": null,
"max_length": 50,
"numeric_precision": null,
"numeric_scale": null,
"is_nullable": true,
"is_unique": false,
"is_indexed": false,
"is_primary_key": false,
"is_generated": false,
"generation_expression": null,
"has_auto_increment": false,
"foreign_key_table": null,
"foreign_key_column": null
}
}

View File

@ -0,0 +1,45 @@
{
"collection": "tab",
"field": "id",
"type": "uuid",
"meta": {
"collection": "tab",
"conditions": null,
"display": null,
"display_options": null,
"field": "id",
"group": null,
"hidden": true,
"interface": "input",
"note": null,
"options": null,
"readonly": true,
"required": false,
"sort": 1,
"special": [
"uuid"
],
"translations": null,
"validation": null,
"validation_message": null,
"width": "full"
},
"schema": {
"name": "id",
"table": "tab",
"data_type": "uuid",
"default_value": null,
"max_length": null,
"numeric_precision": null,
"numeric_scale": null,
"is_nullable": false,
"is_unique": true,
"is_indexed": false,
"is_primary_key": true,
"is_generated": false,
"generation_expression": null,
"has_auto_increment": false,
"foreign_key_table": null,
"foreign_key_column": null
}
}

View File

@ -0,0 +1,32 @@
{
"collection": "tab",
"field": "items",
"type": "alias",
"meta": {
"collection": "tab",
"conditions": null,
"display": "related-values",
"display_options": {
"template": "{{collection}}"
},
"field": "items",
"group": null,
"hidden": false,
"interface": "list-m2a",
"note": "Components inside this tab",
"options": {
"enableCreate": true,
"enableSelect": true
},
"readonly": false,
"required": false,
"sort": 6,
"special": [
"m2a"
],
"translations": null,
"validation": null,
"validation_message": null,
"width": "full"
}
}

View File

@ -0,0 +1,43 @@
{
"collection": "tab",
"field": "sort",
"type": "integer",
"meta": {
"collection": "tab",
"conditions": null,
"display": null,
"display_options": null,
"field": "sort",
"group": null,
"hidden": true,
"interface": null,
"note": null,
"options": null,
"readonly": false,
"required": false,
"sort": 4,
"special": null,
"translations": null,
"validation": null,
"validation_message": null,
"width": "full"
},
"schema": {
"name": "sort",
"table": "tab",
"data_type": "integer",
"default_value": null,
"max_length": null,
"numeric_precision": 32,
"numeric_scale": 0,
"is_nullable": true,
"is_unique": false,
"is_indexed": false,
"is_primary_key": false,
"is_generated": false,
"generation_expression": null,
"has_auto_increment": false,
"foreign_key_table": null,
"foreign_key_column": null
}
}

View File

@ -0,0 +1,43 @@
{
"collection": "tab",
"field": "tabs_id",
"type": "uuid",
"meta": {
"collection": "tab",
"conditions": null,
"display": null,
"display_options": null,
"field": "tabs_id",
"group": null,
"hidden": true,
"interface": null,
"note": null,
"options": null,
"readonly": false,
"required": false,
"sort": 5,
"special": null,
"translations": null,
"validation": null,
"validation_message": null,
"width": "full"
},
"schema": {
"name": "tabs_id",
"table": "tab",
"data_type": "uuid",
"default_value": null,
"max_length": null,
"numeric_precision": null,
"numeric_scale": null,
"is_nullable": true,
"is_unique": false,
"is_indexed": false,
"is_primary_key": false,
"is_generated": false,
"generation_expression": null,
"has_auto_increment": false,
"foreign_key_table": "tabs",
"foreign_key_column": "id"
}
}

View File

@ -0,0 +1,43 @@
{
"collection": "tab",
"field": "title",
"type": "string",
"meta": {
"collection": "tab",
"conditions": null,
"display": null,
"display_options": null,
"field": "title",
"group": null,
"hidden": false,
"interface": "input",
"note": "Tab title displayed in the tab header",
"options": null,
"readonly": false,
"required": true,
"sort": 2,
"special": null,
"translations": null,
"validation": null,
"validation_message": null,
"width": "half"
},
"schema": {
"name": "title",
"table": "tab",
"data_type": "character varying",
"default_value": null,
"max_length": 255,
"numeric_precision": null,
"numeric_scale": null,
"is_nullable": false,
"is_unique": false,
"is_indexed": false,
"is_primary_key": false,
"is_generated": false,
"generation_expression": null,
"has_auto_increment": false,
"foreign_key_table": null,
"foreign_key_column": null
}
}

View File

@ -0,0 +1,43 @@
{
"collection": "tab_items",
"field": "collection",
"type": "string",
"meta": {
"collection": "tab_items",
"conditions": null,
"display": null,
"display_options": null,
"field": "collection",
"group": null,
"hidden": true,
"interface": null,
"note": null,
"options": null,
"readonly": false,
"required": false,
"sort": 4,
"special": null,
"translations": null,
"validation": null,
"validation_message": null,
"width": "full"
},
"schema": {
"name": "collection",
"table": "tab_items",
"data_type": "character varying",
"default_value": null,
"max_length": 255,
"numeric_precision": null,
"numeric_scale": null,
"is_nullable": true,
"is_unique": false,
"is_indexed": false,
"is_primary_key": false,
"is_generated": false,
"generation_expression": null,
"has_auto_increment": false,
"foreign_key_table": null,
"foreign_key_column": null
}
}

View File

@ -0,0 +1,43 @@
{
"collection": "tab_items",
"field": "id",
"type": "integer",
"meta": {
"collection": "tab_items",
"conditions": null,
"display": null,
"display_options": null,
"field": "id",
"group": null,
"hidden": true,
"interface": "input",
"note": null,
"options": null,
"readonly": true,
"required": false,
"sort": 1,
"special": null,
"translations": null,
"validation": null,
"validation_message": null,
"width": "full"
},
"schema": {
"name": "id",
"table": "tab_items",
"data_type": "integer",
"default_value": "nextval('tab_items_id_seq'::regclass)",
"max_length": null,
"numeric_precision": 32,
"numeric_scale": 0,
"is_nullable": false,
"is_unique": true,
"is_indexed": false,
"is_primary_key": true,
"is_generated": false,
"generation_expression": null,
"has_auto_increment": true,
"foreign_key_table": null,
"foreign_key_column": null
}
}

View File

@ -0,0 +1,43 @@
{
"collection": "tab_items",
"field": "item",
"type": "string",
"meta": {
"collection": "tab_items",
"conditions": null,
"display": null,
"display_options": null,
"field": "item",
"group": null,
"hidden": true,
"interface": null,
"note": null,
"options": null,
"readonly": false,
"required": false,
"sort": 3,
"special": null,
"translations": null,
"validation": null,
"validation_message": null,
"width": "full"
},
"schema": {
"name": "item",
"table": "tab_items",
"data_type": "character varying",
"default_value": null,
"max_length": 255,
"numeric_precision": null,
"numeric_scale": null,
"is_nullable": true,
"is_unique": false,
"is_indexed": false,
"is_primary_key": false,
"is_generated": false,
"generation_expression": null,
"has_auto_increment": false,
"foreign_key_table": null,
"foreign_key_column": null
}
}

View File

@ -0,0 +1,43 @@
{
"collection": "tab_items",
"field": "sort",
"type": "integer",
"meta": {
"collection": "tab_items",
"conditions": null,
"display": null,
"display_options": null,
"field": "sort",
"group": null,
"hidden": true,
"interface": null,
"note": null,
"options": null,
"readonly": false,
"required": false,
"sort": 5,
"special": null,
"translations": null,
"validation": null,
"validation_message": null,
"width": "full"
},
"schema": {
"name": "sort",
"table": "tab_items",
"data_type": "integer",
"default_value": null,
"max_length": null,
"numeric_precision": 32,
"numeric_scale": 0,
"is_nullable": true,
"is_unique": false,
"is_indexed": false,
"is_primary_key": false,
"is_generated": false,
"generation_expression": null,
"has_auto_increment": false,
"foreign_key_table": null,
"foreign_key_column": null
}
}

View File

@ -0,0 +1,43 @@
{
"collection": "tab_items",
"field": "tab_id",
"type": "uuid",
"meta": {
"collection": "tab_items",
"conditions": null,
"display": null,
"display_options": null,
"field": "tab_id",
"group": null,
"hidden": true,
"interface": null,
"note": null,
"options": null,
"readonly": false,
"required": false,
"sort": 2,
"special": null,
"translations": null,
"validation": null,
"validation_message": null,
"width": "full"
},
"schema": {
"name": "tab_id",
"table": "tab_items",
"data_type": "uuid",
"default_value": null,
"max_length": null,
"numeric_precision": null,
"numeric_scale": null,
"is_nullable": true,
"is_unique": false,
"is_indexed": false,
"is_primary_key": false,
"is_generated": false,
"generation_expression": null,
"has_auto_increment": false,
"foreign_key_table": "tab",
"foreign_key_column": "id"
}
}

View File

@ -0,0 +1,45 @@
{
"collection": "tabs",
"field": "icon_as_labels",
"type": "boolean",
"meta": {
"collection": "tabs",
"conditions": null,
"display": null,
"display_options": null,
"field": "icon_as_labels",
"group": null,
"hidden": false,
"interface": "boolean",
"note": "Show icons only when tab is not active",
"options": null,
"readonly": false,
"required": false,
"sort": 2,
"special": [
"cast-boolean"
],
"translations": null,
"validation": null,
"validation_message": null,
"width": "half"
},
"schema": {
"name": "icon_as_labels",
"table": "tabs",
"data_type": "boolean",
"default_value": false,
"max_length": null,
"numeric_precision": null,
"numeric_scale": null,
"is_nullable": true,
"is_unique": false,
"is_indexed": false,
"is_primary_key": false,
"is_generated": false,
"generation_expression": null,
"has_auto_increment": false,
"foreign_key_table": null,
"foreign_key_column": null
}
}

View File

@ -0,0 +1,45 @@
{
"collection": "tabs",
"field": "id",
"type": "uuid",
"meta": {
"collection": "tabs",
"conditions": null,
"display": null,
"display_options": null,
"field": "id",
"group": null,
"hidden": true,
"interface": "input",
"note": null,
"options": null,
"readonly": true,
"required": false,
"sort": 1,
"special": [
"uuid"
],
"translations": null,
"validation": null,
"validation_message": null,
"width": "full"
},
"schema": {
"name": "id",
"table": "tabs",
"data_type": "uuid",
"default_value": null,
"max_length": null,
"numeric_precision": null,
"numeric_scale": null,
"is_nullable": false,
"is_unique": true,
"is_indexed": false,
"is_primary_key": true,
"is_generated": false,
"generation_expression": null,
"has_auto_increment": false,
"foreign_key_table": null,
"foreign_key_column": null
}
}

View File

@ -0,0 +1,33 @@
{
"collection": "tabs",
"field": "tabs",
"type": "alias",
"meta": {
"collection": "tabs",
"conditions": null,
"display": "related-values",
"display_options": {
"template": "{{title}}"
},
"field": "tabs",
"group": null,
"hidden": false,
"interface": "list-o2m",
"note": "Individual tabs in this container",
"options": {
"enableCreate": true,
"enableSelect": false,
"template": "{{title}}"
},
"readonly": false,
"required": false,
"sort": 4,
"special": [
"o2m"
],
"translations": null,
"validation": null,
"validation_message": null,
"width": "full"
}
}

View File

@ -0,0 +1,54 @@
{
"collection": "tags_component",
"field": "dataField",
"type": "string",
"meta": {
"collection": "tags_component",
"conditions": null,
"display": null,
"display_options": null,
"field": "dataField",
"group": null,
"hidden": false,
"interface": "select-dropdown",
"note": "Which field to use: offers or needs",
"options": {
"choices": [
{
"text": "Offers",
"value": "offers"
},
{
"text": "Needs",
"value": "needs"
}
]
},
"readonly": false,
"required": true,
"sort": 2,
"special": null,
"translations": null,
"validation": null,
"validation_message": null,
"width": "half"
},
"schema": {
"name": "dataField",
"table": "tags_component",
"data_type": "character varying",
"default_value": "offers",
"max_length": 255,
"numeric_precision": null,
"numeric_scale": null,
"is_nullable": false,
"is_unique": false,
"is_indexed": false,
"is_primary_key": false,
"is_generated": false,
"generation_expression": null,
"has_auto_increment": false,
"foreign_key_table": null,
"foreign_key_column": null
}
}

View File

@ -0,0 +1,43 @@
{
"collection": "tags_component",
"field": "heading",
"type": "string",
"meta": {
"collection": "tags_component",
"conditions": null,
"display": null,
"display_options": null,
"field": "heading",
"group": null,
"hidden": false,
"interface": "input",
"note": null,
"options": null,
"readonly": false,
"required": false,
"sort": 3,
"special": null,
"translations": null,
"validation": null,
"validation_message": null,
"width": "half"
},
"schema": {
"name": "heading",
"table": "tags_component",
"data_type": "character varying",
"default_value": null,
"max_length": 255,
"numeric_precision": null,
"numeric_scale": null,
"is_nullable": true,
"is_unique": false,
"is_indexed": false,
"is_primary_key": false,
"is_generated": false,
"generation_expression": null,
"has_auto_increment": false,
"foreign_key_table": null,
"foreign_key_column": null
}
}

View File

@ -0,0 +1,45 @@
{
"collection": "tags_component",
"field": "hideWhenEmpty",
"type": "boolean",
"meta": {
"collection": "tags_component",
"conditions": null,
"display": null,
"display_options": null,
"field": "hideWhenEmpty",
"group": null,
"hidden": false,
"interface": "boolean",
"note": null,
"options": null,
"readonly": false,
"required": false,
"sort": 4,
"special": [
"cast-boolean"
],
"translations": null,
"validation": null,
"validation_message": null,
"width": "half"
},
"schema": {
"name": "hideWhenEmpty",
"table": "tags_component",
"data_type": "boolean",
"default_value": true,
"max_length": null,
"numeric_precision": null,
"numeric_scale": null,
"is_nullable": true,
"is_unique": false,
"is_indexed": false,
"is_primary_key": false,
"is_generated": false,
"generation_expression": null,
"has_auto_increment": false,
"foreign_key_table": null,
"foreign_key_column": null
}
}

View File

@ -0,0 +1,45 @@
{
"collection": "tags_component",
"field": "id",
"type": "uuid",
"meta": {
"collection": "tags_component",
"conditions": null,
"display": null,
"display_options": null,
"field": "id",
"group": null,
"hidden": true,
"interface": "input",
"note": null,
"options": null,
"readonly": true,
"required": false,
"sort": 1,
"special": [
"uuid"
],
"translations": null,
"validation": null,
"validation_message": null,
"width": "full"
},
"schema": {
"name": "id",
"table": "tags_component",
"data_type": "uuid",
"default_value": null,
"max_length": null,
"numeric_precision": null,
"numeric_scale": null,
"is_nullable": false,
"is_unique": true,
"is_indexed": false,
"is_primary_key": true,
"is_generated": false,
"generation_expression": null,
"has_auto_increment": false,
"foreign_key_table": null,
"foreign_key_column": null
}
}

View File

@ -0,0 +1,43 @@
{
"collection": "tags_component",
"field": "placeholder",
"type": "string",
"meta": {
"collection": "tags_component",
"conditions": null,
"display": null,
"display_options": null,
"field": "placeholder",
"group": null,
"hidden": false,
"interface": "input",
"note": "Placeholder text for the input field",
"options": null,
"readonly": false,
"required": false,
"sort": 5,
"special": null,
"translations": null,
"validation": null,
"validation_message": null,
"width": "full"
},
"schema": {
"name": "placeholder",
"table": "tags_component",
"data_type": "character varying",
"default_value": null,
"max_length": 255,
"numeric_precision": null,
"numeric_scale": null,
"is_nullable": true,
"is_unique": false,
"is_indexed": false,
"is_primary_key": false,
"is_generated": false,
"generation_expression": null,
"has_auto_increment": false,
"foreign_key_table": null,
"foreign_key_column": null
}
}

View File

@ -0,0 +1,25 @@
{
"collection": "tab",
"field": "tabs_id",
"related_collection": "tabs",
"meta": {
"junction_field": null,
"many_collection": "tab",
"many_field": "tabs_id",
"one_allowed_collections": null,
"one_collection": "tabs",
"one_collection_field": null,
"one_deselect_action": "nullify",
"one_field": "tabs",
"sort_field": "sort"
},
"schema": {
"table": "tab",
"column": "tabs_id",
"foreign_key_table": "tabs",
"foreign_key_column": "id",
"constraint_name": "tab_tabs_id_foreign",
"on_update": "NO ACTION",
"on_delete": "SET NULL"
}
}

View File

@ -0,0 +1,27 @@
{
"collection": "tab_items",
"field": "item",
"related_collection": null,
"meta": {
"junction_field": "tab_id",
"many_collection": "tab_items",
"many_field": "item",
"one_allowed_collections": [
"groupSubheaders",
"contactInfos",
"texts",
"startEnd",
"gallery",
"crowdfundings",
"inviteLinks",
"relations",
"tags_component",
"attestations_component"
],
"one_collection": null,
"one_collection_field": "collection",
"one_deselect_action": "nullify",
"one_field": null,
"sort_field": null
}
}

View File

@ -0,0 +1,25 @@
{
"collection": "tab_items",
"field": "tab_id",
"related_collection": "tab",
"meta": {
"junction_field": "item",
"many_collection": "tab_items",
"many_field": "tab_id",
"one_allowed_collections": null,
"one_collection": "tab",
"one_collection_field": null,
"one_deselect_action": "nullify",
"one_field": "items",
"sort_field": "sort"
},
"schema": {
"table": "tab_items",
"column": "tab_id",
"foreign_key_table": "tab",
"foreign_key_column": "id",
"constraint_name": "tab_items_tab_id_foreign",
"on_update": "NO ACTION",
"on_delete": "SET NULL"
}
}

View File

@ -14,7 +14,10 @@
"gallery",
"crowdfundings",
"inviteLinks",
"relations"
"relations",
"tags_component",
"attestations_component",
"tabs"
],
"one_collection": null,
"one_collection_field": "collection",

View File

@ -66,6 +66,7 @@ export const Autocomplete = ({
heighlightedSuggestion > 0 && setHeighlightedSuggestion((current) => current - 1)
break
case 'Enter':
event.preventDefault()
if (filteredSuggestions.length > 0) {
// eslint-disable-next-line security/detect-object-injection
onSelected(filteredSuggestions[heighlightedSuggestion])
@ -80,7 +81,7 @@ export const Autocomplete = ({
}
return (
<div>
<div className='tw:flex-1'>
<input
ref={inputRef}
{...inputProps}
@ -88,7 +89,7 @@ export const Autocomplete = ({
onChange={(e) => handleChange(e)}
tabIndex='-1'
onKeyDown={handleKeyDown}
className='tw:border-none tw:focus:outline-none tw:focus:ring-0 tw:mt-5'
className='tw:border-none tw:focus:outline-none tw:focus:ring-0 tw:mt-5 tw:w-full'
/>
<ul
className={`tw:absolute tw:z-4000 ${filteredSuggestions.length > 0 && 'tw:bg-base-100 tw:rounded-xl tw:p-2'}`}

View File

@ -22,6 +22,7 @@ import { HeaderView } from '#components/Map/Subcomponents/ItemPopupComponents/He
import { MapOverlayPage } from '#components/Templates'
import { handleDelete, linkItem, unlinkItem } from './itemFunctions'
import { AttestationsContext } from './hooks/useAttestations'
import { FlexView } from './Templates/FlexView'
import { OnepagerView } from './Templates/OnepagerView'
import { SimpleView } from './Templates/SimpleView'
@ -201,7 +202,11 @@ export function ProfileView({ attestationApi }: { attestationApi?: ItemsApi<any>
{template === 'simple' && <SimpleView item={item} />}
{template === 'flex' && <FlexView item={item} />}
{template === 'flex' && (
<AttestationsContext.Provider value={attestations}>
<FlexView item={item} />
</AttestationsContext.Provider>
)}
{template === 'tabs' && (
<TabsView

View File

@ -0,0 +1,93 @@
import { Link } from 'react-router-dom'
import { useAppState } from '#components/AppShell/hooks/useAppState'
import { useItems } from '#components/Map/hooks/useItems'
import { useAttestations } from '#components/Profile/hooks/useAttestations'
import { timeAgo } from '#utils/TimeAgo'
import type { Item } from '#types/Item'
interface Props {
item: Item
heading?: string
hideWhenEmpty?: boolean
}
export const AttestationsView = ({ item, heading = 'Trust', hideWhenEmpty = true }: Props) => {
const attestations = useAttestations()
const items = useItems()
const appState = useAppState()
const getUserProfile = (userId: string) => {
return items.find((i) => i.user_created?.id === userId && i.layer?.userProfileLayer)
}
// Filter attestations for this user
const userAttestations = attestations
.filter((a) => a.to.some((t) => t.directus_users_id === item.user_created?.id))
.sort((a, b) => new Date(b.date_created).getTime() - new Date(a.date_created).getTime())
if (hideWhenEmpty && userAttestations.length === 0) {
return null
}
return (
<div>
<h2 className='tw:text-lg tw:font-bold tw:mb-2'>{heading}</h2>
<table className='sm:tw:table-sm md:tw:table-md tw:w-full'>
<tbody>
{userAttestations.map((a, i) => (
<tr key={i}>
<td>
<div
className={`tw:cursor-pointer tw:text-3xl tw:mask ${a.shape === 'squircle' ? 'tw:mask-squircle' : a.shape === 'circle' ? 'tw:mask-circle' : 'tw:mask-hexagon-2'} tw:p-2 tw:my-2 tw:mr-2 tw:shadow-xl`}
style={{ backgroundColor: a.color }}
>
{a.emoji}
</div>
</td>
<td>
<div className='tw:mr-2'>
<i>{a.text}</i>
</div>
</td>
<td>
{getUserProfile(a.user_created.id) ? (
<Link to={'/item/' + getUserProfile(a.user_created.id)?.id}>
<div className='flex items-center gap-3'>
<div className='tw:avatar'>
<div className='tw:mask tw:rounded-full tw:h-8 tw:w-8 tw:mr-2'>
{getUserProfile(a.user_created.id)?.image && (
<img
src={appState.assetsApi.url + getUserProfile(a.user_created.id)?.image}
alt='Avatar'
/>
)}
</div>
</div>
<div>
<div className='font-bold'>
{getUserProfile(a.user_created.id)?.name ?? a.user_created.first_name}{' '}
</div>
<div className='tw:text-xs opacity-50 tw:text-zinc-500'>
{timeAgo(a.date_created)}
</div>
</div>
</div>
</Link>
) : (
<div>
<div className='font-bold'>{a.user_created.first_name} </div>
<div className='tw:text-xs opacity-50 tw:text-zinc-500'>
{timeAgo(a.date_created)}
</div>
</div>
)}
</td>
</tr>
))}
</tbody>
</table>
</div>
)
}

View File

@ -24,7 +24,7 @@ export const ContactInfoView = ({ item, heading }: { item: Item; heading: string
}, [item, items])
return (
<div className='tw:bg-base-200 tw:mb-6 tw:mt-6 tw:p-6'>
<div className='tw:bg-base-200 tw:p-6'>
<h2 className='tw:text-lg tw:font-semibold'>{heading}</h2>
<div className='tw:mt-4 tw:flex tw:items-center'>
{profileOwner?.image && (

View File

@ -144,7 +144,7 @@ export const CrowdfundingView = ({ item }: { item: Item }) => {
const currentBalance = balanceValueInCents
return (
<div className='tw:mx-6 tw:mb-6'>
<div>
<div className='tw:card tw:bg-base-200 tw:w-fit tw:max-w-full tw:shadow'>
<div className='tw:stats tw:bg-base-200 tw:stats-horizontal tw:rounded-b-none'>
<div className='tw:stat tw:p-3'>

View File

@ -40,7 +40,7 @@ export const GalleryView = ({ item }: { item: Item }) => {
if (images.length > 0)
return (
<div className='tw:mx-6 tw:mb-6'>
<div className=''>
<RowsPhotoAlbum
photos={images}
targetRowHeight={150}

View File

@ -11,7 +11,7 @@ export const GroupSubHeaderView = ({
shareBaseUrl: string
platforms?: string[]
}) => (
<div className='tw:px-6'>
<div>
<div className='tw:float-left tw:mt-2 tw:mb-4 tw:flex tw:items-center'>
{item.status && (
<div className='tw:mt-1.5'>

View File

@ -17,7 +17,7 @@ export const InviteLinkView = ({ item }: { item: Item }) => {
}
return (
<div className='tw:my-10 tw:mt-2 tw:px-6'>
<div>
<h2 className='tw:text-lg tw:font-semibold'>Invite</h2>
<div className='tw:mt-2 tw:text-sm tw:flex tw:gap-2 tw:mb-2'>
<input

View File

@ -4,7 +4,7 @@ import type { Item } from '#types/Item'
export const ProfileStartEndView = ({ item }: { item: Item }) => {
return (
<div className='tw:mt-2 tw:px-6'>
<div>
<StartEndView item={item}></StartEndView>
</div>
)

View File

@ -0,0 +1,41 @@
import { TagsWidget } from '#components/Profile/Subcomponents/TagsWidget'
import type { FormState } from '#types/FormState'
import type { Item } from '#types/Item'
interface Props {
item: Item
state: FormState
setState: React.Dispatch<React.SetStateAction<FormState>>
dataField: 'offers' | 'needs'
heading?: string
placeholder?: string
}
export const ProfileTagsForm = ({
state,
setState,
dataField,
heading,
placeholder,
}: Props) => {
const defaultHeading = dataField === 'offers' ? 'Offers' : 'Needs'
const defaultPlaceholder = dataField === 'offers' ? 'enter your offers' : 'enter your needs'
return (
<div className='tw:flex-1 tw:flex tw:flex-col tw:min-h-0'>
<h3 className='tw:text-base tw:font-semibold tw:mt-4 tw:mb-2 tw:flex-none'>{heading ?? defaultHeading}</h3>
<TagsWidget
defaultTags={state[dataField]}
onUpdate={(tags) =>
setState((prevState) => ({
...prevState,
[dataField]: tags,
}))
}
placeholder={placeholder ?? defaultPlaceholder}
containerStyle='tw:bg-transparent tw:w-full tw:flex-1 tw:text-xs tw:pb-2 tw:overflow-auto'
/>
</div>
)
}

View File

@ -0,0 +1,56 @@
import { useAddFilterTag } from '#components/Map/hooks/useFilter'
import { useTags } from '#components/Map/hooks/useTags'
import { TagView } from '#components/Templates/TagView'
import type { Item } from '#types/Item'
import type { Tag } from '#types/Tag'
interface Props {
item: Item
dataField: 'offers' | 'needs'
heading?: string
hideWhenEmpty?: boolean
}
export const ProfileTagsView = ({
item,
dataField,
heading,
hideWhenEmpty = true,
}: Props) => {
const addFilterTag = useAddFilterTag()
const allTags = useTags()
// Get the tag IDs from the item based on dataField
const tagRelations = item[dataField] ?? []
// Resolve tag IDs to full Tag objects
const tags: Tag[] = tagRelations.reduce((acc: Tag[], relation) => {
const tag = allTags.find((t) => t.id === relation.tags_id)
if (tag) acc.push(tag)
return acc
}, [])
if (hideWhenEmpty && tags.length === 0) {
return null
}
const defaultHeading = dataField === 'offers' ? 'Offers' : 'Needs'
return (
<div>
<h2 className='tw:text-lg tw:font-bold tw:-mb-2'>{heading ?? defaultHeading}</h2>
<div className='tw:flex tw:flex-wrap tw:mb-4'>
{tags.map((tag) => (
<TagView
key={tag.id}
tag={tag}
onClick={() => {
addFilterTag(tag)
}}
/>
))}
</div>
</div>
)
}

View File

@ -21,7 +21,7 @@ export const ProfileTextView = ({
const shouldShowHeading = !(hideWhenEmpty && (text === '' || text === null))
return (
<div className='tw:my-10 tw:mt-2 tw:px-6'>
<div>
{shouldShowHeading && <h2 className='tw:text-lg tw:font-semibold'>{heading}</h2>}
<div className='tw:mt-2 tw:text-sm'>
<TextView item={item} text={text as string | null | undefined} itemId={item.id} />

View File

@ -60,7 +60,7 @@ export const RelationsView = ({
}
return (
<div className='tw:my-10 tw:mt-2 tw:px-6'>
<div>
<h2 className='tw:text-lg tw:font-bold'>{heading}</h2>
{hasRelatedItems ? (
<ul>

View File

@ -0,0 +1,133 @@
import { useCallback, useEffect, useState } from 'react'
import { useLocation, useNavigate } from 'react-router-dom'
import { ContactInfoForm } from '#components/Profile/Subcomponents/ContactInfoForm'
import { CrowdfundingForm } from '#components/Profile/Subcomponents/CrowdfundingForm'
import { GalleryForm } from '#components/Profile/Subcomponents/GalleryForm'
import { GroupSubheaderForm } from '#components/Profile/Subcomponents/GroupSubheaderForm'
import { ProfileStartEndForm } from '#components/Profile/Subcomponents/ProfileStartEndForm'
import { ProfileTextForm } from '#components/Profile/Subcomponents/ProfileTextForm'
import { ProfileTagsForm } from '#components/Profile/Subcomponents/ProfileTagsForm'
import type { FormState } from '#types/FormState'
import type { Item } from '#types/Item'
import type { Key } from 'react'
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type ComponentMap = Record<string, React.ComponentType<any>>
const componentMap: ComponentMap = {
groupSubheaders: GroupSubheaderForm,
texts: ProfileTextForm,
contactInfos: ContactInfoForm,
startEnd: ProfileStartEndForm,
crowdfundings: CrowdfundingForm,
gallery: GalleryForm,
inviteLinks: () => null,
relations: () => null, // Relations are not editable in form
tags_component: ProfileTagsForm,
attestations_component: () => null, // Attestations are view-only
}
interface TabItem {
id: string
title: string
icon?: string
// eslint-disable-next-line @typescript-eslint/no-explicit-any
items: Array<{ collection: string; id: Key | null | undefined; item: any }>
}
interface Props {
item: Item
state: FormState
setState: React.Dispatch<React.SetStateAction<FormState>>
tabs: TabItem[]
icon_as_labels?: boolean
}
export const TabsContainerForm = ({
item,
state,
setState,
tabs,
icon_as_labels = false,
}: Props) => {
const location = useLocation()
const navigate = useNavigate()
const [activeTab, setActiveTab] = useState<number>(0)
const tabsLength = tabs?.length ?? 0
useEffect(() => {
if (!tabs || tabs.length === 0) return
const params = new URLSearchParams(location.search)
const urlTab = params.get('tab')
if (urlTab !== null && !isNaN(Number(urlTab))) {
const index = Number(urlTab)
if (index >= 0 && index < tabs.length) {
setActiveTab(index)
}
}
}, [tabs, tabsLength, location.search])
const updateActiveTab = useCallback(
(index: number) => {
setActiveTab(index)
const params = new URLSearchParams(location.search)
params.set('tab', `${index}`)
const newUrl = location.pathname + '?' + params.toString()
navigate(newUrl, { replace: false })
},
[location.pathname, location.search, navigate],
)
if (!tabs || tabs.length === 0) {
return null
}
return (
<div className='tw:flex tw:flex-col tw:flex-1 tw:min-h-0'>
{/* Tabs */}
<div className='tw:flex tw:bg-base-200 tw:rounded-lg tw:p-1 tw:mb-4 tw:flex-none'>
{tabs.map((tab, index) => (
<button
type="button"
key={tab.id}
className={`tw:flex-1 tw:flex tw:items-center tw:justify-center tw:gap-2 tw:py-2 tw:px-4 tw:rounded-md tw:transition-colors tw:cursor-pointer ${activeTab === index ? 'tw:bg-primary tw:text-primary-content' : 'hover:tw:bg-base-300'}`}
onClick={() => updateActiveTab(index)}
onKeyDown={(e) => {
if (e.key === 'Enter') {
e.preventDefault()
updateActiveTab(index)
}
}}
>
{tab.icon && <span>{tab.icon}</span>}
{!(icon_as_labels && activeTab !== index) && <span>{tab.title}</span>}
</button>
))}
</div>
{/* Tab Content */}
<div className='tw:flex-1 tw:flex tw:flex-col tw:min-h-0'>
{tabs[activeTab]?.items.map((templateItem) => {
const TemplateComponent = componentMap[templateItem.collection]
return TemplateComponent ? (
<TemplateComponent
key={templateItem.id}
item={item}
state={state}
setState={setState}
{...templateItem.item}
/>
) : (
<div className='tw:mt-2 tw:flex-none' key={templateItem.id}>
{templateItem.collection} form not found
</div>
)
})}
</div>
</div>
)
}

View File

@ -0,0 +1,115 @@
/* eslint-disable camelcase */
import React, { useCallback, useEffect, useState } from 'react'
import { useLocation, useNavigate } from 'react-router-dom'
import { AttestationsView } from '#components/Profile/Subcomponents/AttestationsView'
import { ContactInfoView } from '#components/Profile/Subcomponents/ContactInfoView'
import { CrowdfundingView } from '#components/Profile/Subcomponents/CrowdfundingView'
import { GalleryView } from '#components/Profile/Subcomponents/GalleryView'
import { GroupSubHeaderView } from '#components/Profile/Subcomponents/GroupSubHeaderView'
import { InviteLinkView } from '#components/Profile/Subcomponents/InviteLinkView'
import { ProfileStartEndView } from '#components/Profile/Subcomponents/ProfileStartEndView'
import { ProfileTagsView } from '#components/Profile/Subcomponents/ProfileTagsView'
import { ProfileTextView } from '#components/Profile/Subcomponents/ProfileTextView'
import { RelationsView } from '#components/Profile/Subcomponents/RelationsView'
import type { Item } from '#types/Item'
import type { Key } from 'react'
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type ComponentMap = Record<string, React.ComponentType<any>>
const componentMap: ComponentMap = {
groupSubheaders: GroupSubHeaderView,
texts: ProfileTextView,
contactInfos: ContactInfoView,
startEnd: ProfileStartEndView,
gallery: GalleryView,
crowdfundings: CrowdfundingView,
inviteLinks: InviteLinkView,
relations: RelationsView,
tags_component: ProfileTagsView,
attestations_component: AttestationsView,
}
interface TabItem {
id: string
title: string
icon?: string
// eslint-disable-next-line @typescript-eslint/no-explicit-any
items: { collection: string; id: Key | null | undefined; item: any }[]
}
interface Props {
item: Item
tabs: TabItem[]
icon_as_labels?: boolean
}
export const TabsContainerView = ({ item, tabs = [], icon_as_labels = false }: Props) => {
const location = useLocation()
const navigate = useNavigate()
const [activeTab, setActiveTab] = useState<number>(0)
const tabsLength = tabs.length
useEffect(() => {
if (tabs.length === 0) return
const params = new URLSearchParams(location.search)
const urlTab = params.get('tab')
if (urlTab !== null && !isNaN(Number(urlTab))) {
const index = Number(urlTab)
if (index >= 0 && index < tabs.length) {
setActiveTab(index)
}
}
}, [tabs, tabsLength, location.search])
const updateActiveTab = useCallback(
(index: number) => {
setActiveTab(index)
const params = new URLSearchParams(location.search)
params.set('tab', `${index}`)
const newUrl = location.pathname + '?' + params.toString()
navigate(newUrl, { replace: false })
},
[location.pathname, location.search, navigate],
)
if (tabs.length === 0) {
return null
}
return (
<div>
{/* Tabs */}
<div className='tw:flex tw:bg-base-200 tw:rounded-lg tw:p-1 tw:mb-4'>
{tabs.map((tab, index) => (
<button
key={tab.id}
className={`tw:flex-1 tw:flex tw:items-center tw:justify-center tw:gap-2 tw:py-2 tw:px-4 tw:rounded-md tw:transition-colors tw:cursor-pointer ${activeTab === index ? 'tw:bg-primary tw:text-primary-content' : 'hover:tw:bg-base-300'}`}
onClick={() => updateActiveTab(index)}
>
{tab.icon && <span>{tab.icon}</span>}
{!(icon_as_labels && activeTab !== index) && <span>{tab.title}</span>}
</button>
))}
</div>
{/* Tab Content */}
<div className='tw:overflow-y-auto fade tw:pb-4 tw:overflow-x-hidden'>
{tabs[activeTab]?.items.map((templateItem) => {
const TemplateComponent = componentMap[templateItem.collection]
return TemplateComponent ? (
<TemplateComponent key={templateItem.id} item={item} {...templateItem.item} />
) : (
<div className='tw:mb-6' key={templateItem.id}>
{templateItem.collection} view not found
</div>
)
})}
</div>
</div>
)
}

View File

@ -97,7 +97,7 @@ export const TagsWidget = ({ placeholder, containerStyle, defaultTags, onUpdate
onKeyDown,
onKeyUp,
onChange,
className: 'tw:bg-transparent tw:w-fit tw:mt-5 tw:h-fit',
className: 'tw:bg-transparent tw:min-w-48 tw:w-fit tw:mt-5 tw:h-fit',
}
/* eslint-disable react/prop-types */

View File

@ -5,7 +5,9 @@ import { CrowdfundingForm } from '#components/Profile/Subcomponents/Crowdfunding
import { GalleryForm } from '#components/Profile/Subcomponents/GalleryForm'
import { GroupSubheaderForm } from '#components/Profile/Subcomponents/GroupSubheaderForm'
import { ProfileStartEndForm } from '#components/Profile/Subcomponents/ProfileStartEndForm'
import { ProfileTagsForm } from '#components/Profile/Subcomponents/ProfileTagsForm'
import { ProfileTextForm } from '#components/Profile/Subcomponents/ProfileTextForm'
import { TabsContainerForm } from '#components/Profile/Subcomponents/TabsContainerForm'
import type { FormState } from '#types/FormState'
import type { Item } from '#types/Item'
@ -17,8 +19,11 @@ const componentMap = {
startEnd: ProfileStartEndForm,
crowdfundings: CrowdfundingForm,
gallery: GalleryForm,
inviteLinks: () => null, // Not needed for now
// weitere Komponenten hier
inviteLinks: () => null,
relations: () => null,
tags_component: ProfileTagsForm,
attestations_component: () => null,
tabs: TabsContainerForm,
}
export const FlexForm = ({

View File

@ -1,13 +1,16 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import { AttestationsView } from '#components/Profile/Subcomponents/AttestationsView'
import { ContactInfoView } from '#components/Profile/Subcomponents/ContactInfoView'
import { CrowdfundingView } from '#components/Profile/Subcomponents/CrowdfundingView'
import { GalleryView } from '#components/Profile/Subcomponents/GalleryView'
import { GroupSubHeaderView } from '#components/Profile/Subcomponents/GroupSubHeaderView'
import { InviteLinkView } from '#components/Profile/Subcomponents/InviteLinkView'
import { ProfileStartEndView } from '#components/Profile/Subcomponents/ProfileStartEndView'
import { ProfileTagsView } from '#components/Profile/Subcomponents/ProfileTagsView'
import { ProfileTextView } from '#components/Profile/Subcomponents/ProfileTextView'
import { RelationsView } from '#components/Profile/Subcomponents/RelationsView'
import { TabsContainerView } from '#components/Profile/Subcomponents/TabsContainerView'
import type { Item } from '#types/Item'
import type { Key } from 'react'
@ -21,12 +24,14 @@ const componentMap = {
crowdfundings: CrowdfundingView,
inviteLinks: InviteLinkView,
relations: RelationsView,
// weitere Komponenten hier
tags_component: ProfileTagsView,
attestations_component: AttestationsView,
tabs: TabsContainerView,
}
export const FlexView = ({ item }: { item: Item }) => {
return (
<div className='tw:h-full tw:overflow-y-auto fade'>
<div className='tw:h-full tw:overflow-y-auto fade tw:px-6 tw:py-4 tw:flex tw:flex-col tw:gap-4'>
{item.layer?.itemType.profileTemplate.map(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(templateItem: { collection: string | number; id: Key | null | undefined; item: any }) => {

View File

@ -0,0 +1,16 @@
import { createContext, useContext } from 'react'
export interface Attestation {
id: string
text: string
emoji: string
color: string
shape: string
date_created: string
user_created: { id: string; first_name: string }
to: Array<{ directus_users_id: string }>
}
export const AttestationsContext = createContext<Attestation[]>([])
export const useAttestations = () => useContext(AttestationsContext)

52
package-lock.json generated
View File

@ -226,7 +226,6 @@
"integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/code-frame": "^7.27.1",
"@babel/generator": "^7.28.3",
@ -2422,7 +2421,6 @@
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz",
"integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==",
"license": "MIT",
"peer": true,
"dependencies": {
"@floating-ui/core": "^1.7.3",
"@floating-ui/utils": "^0.2.10"
@ -3544,7 +3542,6 @@
"integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/core": "^7.21.3",
"@svgr/babel-preset": "8.1.0",
@ -4007,7 +4004,6 @@
"resolved": "https://registry.npmjs.org/@tiptap/core/-/core-3.11.0.tgz",
"integrity": "sha512-kmS7ZVpHm1EMnW1Wmft9H5ZLM7E0G0NGBx+aGEHGDcNxZBXD2ZUa76CuWjIhOGpwsPbELp684ZdpF2JWoNi4Dg==",
"license": "MIT",
"peer": true,
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
@ -4255,7 +4251,6 @@
"resolved": "https://registry.npmjs.org/@tiptap/extension-list/-/extension-list-3.11.0.tgz",
"integrity": "sha512-4Ane7VCVZ+GFOQNuy2nMP+SoWH7EemC3geTTqvgHm1H0tbSosxLJAVaZ9dF06F35RJmYCm+jLJUhRVd156eCRQ==",
"license": "MIT",
"peer": true,
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
@ -4401,7 +4396,6 @@
"resolved": "https://registry.npmjs.org/@tiptap/extensions/-/extensions-3.11.0.tgz",
"integrity": "sha512-g43beA73ZMLezez1st9LEwYrRHZ0FLzlsSlOZKk7sdmtHLmuqWHf4oyb0XAHol1HZIdGv104rYaGNgmQXr1ecQ==",
"license": "MIT",
"peer": true,
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
@ -4416,7 +4410,6 @@
"resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-3.11.0.tgz",
"integrity": "sha512-plCQDLCZIOc92cizB8NNhBRN0szvYR3cx9i5IXo6v9Xsgcun8KHNcJkesc2AyeqdIs0BtOJZaqQ9adHThz8UDw==",
"license": "MIT",
"peer": true,
"dependencies": {
"prosemirror-changeset": "^2.3.0",
"prosemirror-collab": "^1.3.1",
@ -4531,7 +4524,8 @@
"resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz",
"integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==",
"dev": true,
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/@types/babel__core": {
"version": "7.20.5",
@ -4539,7 +4533,6 @@
"integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/parser": "^7.20.7",
"@babel/types": "^7.20.7",
@ -4663,7 +4656,6 @@
"resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.21.tgz",
"integrity": "sha512-TbAd9DaPGSnzp6QvtYngntMZgcRk+igFELwR2N99XZn7RXUdKgsXMR+28bUO0rPsWp8MIu/f47luLIQuSLYv/w==",
"license": "MIT",
"peer": true,
"dependencies": {
"@types/geojson": "*"
}
@ -4743,7 +4735,6 @@
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.26.tgz",
"integrity": "sha512-RFA/bURkcKzx/X9oumPG9Vp3D3JUgus/d0b67KB0t5S/raciymilkOa66olh78MUI92QLbEJevO7rvqU/kjwKA==",
"license": "MIT",
"peer": true,
"dependencies": {
"@types/prop-types": "*",
"csstype": "^3.0.2"
@ -4754,7 +4745,6 @@
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz",
"integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==",
"license": "MIT",
"peer": true,
"peerDependencies": {
"@types/react": "^18.0.0"
}
@ -4881,7 +4871,6 @@
"integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==",
"dev": true,
"license": "BSD-2-Clause",
"peer": true,
"dependencies": {
"@typescript-eslint/scope-manager": "5.62.0",
"@typescript-eslint/types": "5.62.0",
@ -5498,7 +5487,6 @@
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"devOptional": true,
"license": "MIT",
"peer": true,
"bin": {
"acorn": "bin/acorn"
},
@ -6158,7 +6146,6 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.8.9",
"caniuse-lite": "^1.0.30001746",
@ -7247,7 +7234,8 @@
"resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz",
"integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==",
"dev": true,
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/dom-serializer": {
"version": "1.4.1",
@ -7426,7 +7414,6 @@
"integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"ansi-colors": "^4.1.1",
"strip-ansi": "^6.0.1"
@ -7957,7 +7944,6 @@
"integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@rtsao/scc": "^1.1.0",
"array-includes": "^3.1.9",
@ -8039,7 +8025,6 @@
"integrity": "sha512-68PealUpYoHOBh332JLLD9Sj7OQUDkFpmcfqt8R9sySfFSeuGJjMTJQvCRRB96zO3A/PELRLkPrzsHmzEFQQ5A==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.5.0",
"enhanced-resolve": "^5.17.1",
@ -8121,7 +8106,6 @@
"integrity": "sha512-SWKjd+EuvWkYaS+uN2csvj0KoP43YTu7+phKQ5v+xw6+A0gutVX2yqCeCkC3uLCJFiPfR2dD8Es5L7yUsmvEaA==",
"dev": true,
"license": "ISC",
"peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.4.0"
},
@ -10549,8 +10533,7 @@
"version": "1.9.4",
"resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz",
"integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==",
"license": "BSD-2-Clause",
"peer": true
"license": "BSD-2-Clause"
},
"node_modules/leaflet.locatecontrol": {
"version": "0.79.0",
@ -11113,6 +11096,7 @@
"integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==",
"dev": true,
"license": "MIT",
"peer": true,
"bin": {
"lz-string": "bin/bin.js"
}
@ -11159,7 +11143,6 @@
"resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-5.9.0.tgz",
"integrity": "sha512-YxW9glb/YrDXGDhqy1u+aG113+L86ttAUpTd6sCkGHyUKMXOX8qbGHJQVqxOczy+4CtRKnqcCfSura2MzB0nQA==",
"license": "BSD-3-Clause",
"peer": true,
"dependencies": {
"@mapbox/geojson-rewind": "^0.5.2",
"@mapbox/jsonlint-lines-primitives": "^2.0.2",
@ -12628,7 +12611,6 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"nanoid": "^3.3.11",
"picocolors": "^1.1.1",
@ -13264,7 +13246,6 @@
"integrity": "sha512-QgODejq9K3OzoBbuyobZlUhznP5SKwPqp+6Q6xw6o8gnhr4O85L2U915iM2IDcfF2NPXVaM9zlo9tdwipnYwzg==",
"dev": true,
"license": "MIT",
"peer": true,
"bin": {
"prettier": "bin/prettier.cjs"
},
@ -13307,6 +13288,7 @@
"integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"ansi-regex": "^5.0.1",
"ansi-styles": "^5.0.0",
@ -13322,6 +13304,7 @@
"integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=10"
},
@ -13488,7 +13471,6 @@
"resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.25.3.tgz",
"integrity": "sha512-dY2HdaNXlARknJbrManZ1WyUtos+AP97AmvqdOQtWtrrC5g4mohVX5DTi9rXNFSk09eczLq9GuNTtq3EfMeMGA==",
"license": "MIT",
"peer": true,
"dependencies": {
"orderedmap": "^2.0.0"
}
@ -13518,7 +13500,6 @@
"resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.3.tgz",
"integrity": "sha512-goFKORVbvPuAQaXhpbemJFRKJ2aixr+AZMGiquiqKxaucC6hlpHNZHWgz5R7dS4roHiwq9vDctE//CZ++o0W1Q==",
"license": "MIT",
"peer": true,
"dependencies": {
"prosemirror-model": "^1.0.0",
"prosemirror-transform": "^1.0.0",
@ -13567,7 +13548,6 @@
"resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.41.3.tgz",
"integrity": "sha512-SqMiYMUQNNBP9kfPhLO8WXEk/fon47vc52FQsUiJzTBuyjKgEcoAwMyF04eQ4WZ2ArMn7+ReypYL60aKngbACQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"prosemirror-model": "^1.20.0",
"prosemirror-state": "^1.0.0",
@ -13699,7 +13679,6 @@
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"loose-envify": "^1.1.0"
},
@ -13722,7 +13701,6 @@
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
"integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
"license": "MIT",
"peer": true,
"dependencies": {
"loose-envify": "^1.1.0",
"scheduler": "^0.23.2"
@ -13806,14 +13784,14 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
"dev": true,
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/react-leaflet": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-4.2.1.tgz",
"integrity": "sha512-p9chkvhcKrWn/H/1FFeVSqLdReGwn2qmiobOQGO3BifX+/vV/39qhY8dGqbdcPh1e6jxh/QHriLXr7a4eLFK4Q==",
"license": "Hippocratic-2.1",
"peer": true,
"dependencies": {
"@react-leaflet/core": "^2.1.0"
},
@ -13947,7 +13925,6 @@
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.1.tgz",
"integrity": "sha512-llKsgOkZdbPU1Eg3zK8lCn+sjD9wMRZZPuzmdWWX5SUs8OFkN5HnFVC0u5KMeMaC9aoancFI/KoLuKPqN+hxHw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@remix-run/router": "1.23.0",
"react-router": "6.30.1"
@ -14271,7 +14248,6 @@
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz",
"integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==",
"license": "MIT",
"peer": true,
"dependencies": {
"@types/estree": "1.0.8"
},
@ -15953,7 +15929,6 @@
"integrity": "sha512-/z585740YHURLl9DN2jCWe6OW7zKYm6VoQ93H0sxZ1cwHQEQrUn5BJrEnkWhfzUdyO+BLGjnKUZ9iz9hKloFDw==",
"dev": true,
"license": "Apache-2.0",
"peer": true,
"dependencies": {
"@gerrit0/mini-shiki": "^1.24.0",
"lunr": "^2.3.9",
@ -16026,7 +16001,6 @@
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"devOptional": true,
"license": "Apache-2.0",
"peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@ -16228,7 +16202,6 @@
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"peer": true,
"dependencies": {
"napi-postinstall": "^0.3.0"
},
@ -16407,7 +16380,6 @@
"resolved": "https://registry.npmjs.org/vite/-/vite-7.2.4.tgz",
"integrity": "sha512-NL8jTlbo0Tn4dUEXEsUg8KeyG/Lkmc4Fnzb8JXN/Ykm9G4HNImjtABMJgkQoVjOBN/j2WAwDTRytdqJbZsah7w==",
"license": "MIT",
"peer": true,
"dependencies": {
"esbuild": "^0.25.0",
"fdir": "^6.5.0",
@ -16584,7 +16556,6 @@
"integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@types/chai": "^5.2.2",
"@vitest/expect": "3.2.4",
@ -17061,7 +17032,6 @@
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
@ -17116,7 +17086,6 @@
"integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==",
"dev": true,
"license": "MIT",
"peer": true,
"bin": {
"rollup": "dist/bin/rollup"
},
@ -17412,7 +17381,6 @@
"integrity": "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==",
"dev": true,
"license": "MIT",
"peer": true,
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}