@portaki/module-sections 1.0.0 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +13 -7
- package/portaki.module.json +43 -5
- package/src/components/SectionsGuestView.tsx +4 -4
- package/src/index.tsx +1 -1
- package/src/manifest.test.ts +8 -0
- package/src/module.test.tsx +20 -0
- package/vitest.config.ts +3 -0
- package/backend/pom.xml +0 -57
- package/backend/src/main/java/app/portaki/module/sections/SectionsGatewayModule.java +0 -251
- package/backend/src/main/java/app/portaki/module/sections/SectionsModuleBackendConfiguration.java +0 -8
- package/backend/target/classes/app/portaki/module/sections/SectionsGatewayModule.class +0 -0
- package/backend/target/classes/app/portaki/module/sections/SectionsModuleBackendConfiguration.class +0 -0
- package/backend/target/maven-archiver/pom.properties +0 -3
- package/backend/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst +0 -2
- package/backend/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst +0 -2
- package/backend/target/sections-backend-0.1.0.jar +0 -0
- package/db/README.md +0 -8
- package/db/schema.sql +0 -18
package/package.json
CHANGED
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@portaki/module-sections",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "Portaki module — editorial sections (TipTap)",
|
|
5
5
|
"license": "AGPL-3.0",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "./src/index.tsx",
|
|
8
|
-
"scripts": {
|
|
9
|
-
"lint": "echo \"no lint yet\""
|
|
10
|
-
},
|
|
11
8
|
"dependencies": {
|
|
12
|
-
"@portaki/module-sdk": "0.
|
|
9
|
+
"@portaki/module-sdk": "^1.0.0",
|
|
13
10
|
"@portaki/sdk": "^0.5.0",
|
|
14
11
|
"@tiptap/core": "^3.0.0",
|
|
15
12
|
"@tiptap/html": "^3.0.0",
|
|
@@ -19,7 +16,11 @@
|
|
|
19
16
|
},
|
|
20
17
|
"devDependencies": {
|
|
21
18
|
"@types/react": "^19",
|
|
22
|
-
"typescript": "^5"
|
|
19
|
+
"typescript": "^5",
|
|
20
|
+
"vitest": "^3.0.5",
|
|
21
|
+
"@testing-library/react": "^16.3.0",
|
|
22
|
+
"@testing-library/jest-dom": "^6.6.3",
|
|
23
|
+
"jsdom": "^26.0.0"
|
|
23
24
|
},
|
|
24
25
|
"repository": {
|
|
25
26
|
"type": "git",
|
|
@@ -28,5 +29,10 @@
|
|
|
28
29
|
},
|
|
29
30
|
"publishConfig": {
|
|
30
31
|
"access": "public"
|
|
32
|
+
},
|
|
33
|
+
"scripts": {
|
|
34
|
+
"lint": "echo \"no lint yet\"",
|
|
35
|
+
"test": "vitest run",
|
|
36
|
+
"test:watch": "vitest"
|
|
31
37
|
}
|
|
32
|
-
}
|
|
38
|
+
}
|
package/portaki.module.json
CHANGED
|
@@ -9,7 +9,26 @@
|
|
|
9
9
|
"fr": "Blocs de contenu riches (TipTap) pour le carnet d’accueil invité.",
|
|
10
10
|
"en": "Rich content blocks (TipTap) for the guest welcome book."
|
|
11
11
|
},
|
|
12
|
-
"version": "1.
|
|
12
|
+
"version": "1.2.1",
|
|
13
|
+
"releaseNotesUrl": "https://github.com/PortakiApp/portaki-modules/releases",
|
|
14
|
+
"changelog": [
|
|
15
|
+
{
|
|
16
|
+
"version": "1.2.0",
|
|
17
|
+
"date": "2026-05-18",
|
|
18
|
+
"notes": {
|
|
19
|
+
"fr": "Release catalogue modules 1.2.0.",
|
|
20
|
+
"en": "Module catalog release 1.2.0."
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"version": "1.0.0",
|
|
25
|
+
"date": "2026-05-16",
|
|
26
|
+
"notes": {
|
|
27
|
+
"fr": "Première version : sections TipTap, gateway hôte et invité.",
|
|
28
|
+
"en": "Initial release: TipTap sections, host and guest gateway."
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
],
|
|
13
32
|
"author": {
|
|
14
33
|
"name": "Portaki",
|
|
15
34
|
"url": "https://portaki.app",
|
|
@@ -17,9 +36,16 @@
|
|
|
17
36
|
},
|
|
18
37
|
"icon": "file-text",
|
|
19
38
|
"type": "official",
|
|
20
|
-
"tags": [
|
|
39
|
+
"tags": [
|
|
40
|
+
"editorial",
|
|
41
|
+
"content",
|
|
42
|
+
"sections"
|
|
43
|
+
],
|
|
21
44
|
"portakiVersionMin": "1.0.0",
|
|
22
|
-
"requiresHostSdk": "
|
|
45
|
+
"requiresHostSdk": "1.1.0",
|
|
46
|
+
"database": {
|
|
47
|
+
"schemaVersion": "1.0.0"
|
|
48
|
+
},
|
|
23
49
|
"license": "AGPL-3.0",
|
|
24
50
|
"repository": "https://github.com/PortakiApp/portaki-modules",
|
|
25
51
|
"scopes": [
|
|
@@ -68,7 +94,10 @@
|
|
|
68
94
|
{
|
|
69
95
|
"type": "property-workspace-tab",
|
|
70
96
|
"pathSegment": "sections",
|
|
71
|
-
"label": {
|
|
97
|
+
"label": {
|
|
98
|
+
"fr": "Sections",
|
|
99
|
+
"en": "Sections"
|
|
100
|
+
},
|
|
72
101
|
"icon": "file-text"
|
|
73
102
|
}
|
|
74
103
|
],
|
|
@@ -83,6 +112,15 @@
|
|
|
83
112
|
},
|
|
84
113
|
"npmPackage": "@portaki/module-sections",
|
|
85
114
|
"npmUrl": "https://www.npmjs.com/package/@portaki/module-sections",
|
|
86
|
-
"javaArtifact": "app.portaki.module:sections-backend
|
|
115
|
+
"javaArtifact": "app.portaki.module:sections-backend"
|
|
116
|
+
},
|
|
117
|
+
"runtime": {
|
|
118
|
+
"backend": "wasm",
|
|
119
|
+
"guest": "remote-esm"
|
|
120
|
+
},
|
|
121
|
+
"artifacts": {
|
|
122
|
+
"wasmUrl": "oci://ghcr.io/portakiapp/portaki-module-sections:1.2.0",
|
|
123
|
+
"jarMaven": "app.portaki.module:sections-backend:1.2.0",
|
|
124
|
+
"guestEsmUrl": "https://esm.sh/@portaki/module-sections@1.2.0"
|
|
87
125
|
}
|
|
88
126
|
}
|
|
@@ -33,17 +33,17 @@ function contentToHtml(raw: unknown): string {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
export function SectionsGuestView({ lang }: { lang: LangCode }) {
|
|
36
|
-
const { data,
|
|
36
|
+
const { data, loading, error } = usePortakiQuery<ListResponse>('sections.list', {})
|
|
37
37
|
|
|
38
|
-
if (
|
|
38
|
+
if (loading) {
|
|
39
39
|
return (
|
|
40
|
-
<ModuleSection>
|
|
40
|
+
<ModuleSection title={lang === 'fr' ? 'Sections' : 'Sections'}>
|
|
41
41
|
<p className="text-sm opacity-70">{lang === 'fr' ? 'Chargement…' : 'Loading…'}</p>
|
|
42
42
|
</ModuleSection>
|
|
43
43
|
)
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
if (
|
|
46
|
+
if (error != null || !data?.sections?.length) {
|
|
47
47
|
return null
|
|
48
48
|
}
|
|
49
49
|
|
package/src/index.tsx
CHANGED
|
@@ -10,7 +10,7 @@ export default definePortakiModule({
|
|
|
10
10
|
fr: 'Contenus éditoriaux du carnet d’accueil.',
|
|
11
11
|
en: 'Welcome book editorial content.',
|
|
12
12
|
},
|
|
13
|
-
version: '1.
|
|
13
|
+
version: '1.2.0',
|
|
14
14
|
icon: 'file-text',
|
|
15
15
|
navSlot: 'section',
|
|
16
16
|
render: ({ lang }: ModuleContext) => <SectionsGuestView lang={lang} />,
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { waitFor } from '@testing-library/react'
|
|
3
|
+
import { assertGuestSurface, renderGuestModule } from '@portaki/module-test-support'
|
|
4
|
+
|
|
5
|
+
import moduleDef from './index'
|
|
6
|
+
|
|
7
|
+
describe('@portaki/module-sections', () => {
|
|
8
|
+
it('exposes a valid guest module definition', () => {
|
|
9
|
+
assertGuestSurface(moduleDef)
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
it('renders without crashing', async () => {
|
|
13
|
+
const view =
|
|
14
|
+
moduleDef.surface === 'host' ? renderHostModule(moduleDef) : renderGuestModule(moduleDef)
|
|
15
|
+
await waitFor(() => {
|
|
16
|
+
expect(view.container).toBeTruthy()
|
|
17
|
+
})
|
|
18
|
+
view.unmount()
|
|
19
|
+
})
|
|
20
|
+
})
|
package/vitest.config.ts
ADDED
package/backend/pom.xml
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
-
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
|
3
|
-
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
4
|
-
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
5
|
-
<modelVersion>4.0.0</modelVersion>
|
|
6
|
-
|
|
7
|
-
<groupId>app.portaki.module</groupId>
|
|
8
|
-
<artifactId>sections-backend</artifactId>
|
|
9
|
-
<version>0.1.0</version>
|
|
10
|
-
<packaging>jar</packaging>
|
|
11
|
-
|
|
12
|
-
<name>Sections module (Java backend)</name>
|
|
13
|
-
<description>Gateway handlers and persistence for the Portaki sections editorial module.</description>
|
|
14
|
-
|
|
15
|
-
<properties>
|
|
16
|
-
<java.version>21</java.version>
|
|
17
|
-
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
|
18
|
-
</properties>
|
|
19
|
-
|
|
20
|
-
<dependencies>
|
|
21
|
-
<dependency>
|
|
22
|
-
<groupId>app.portaki</groupId>
|
|
23
|
-
<artifactId>portaki-module-sdk</artifactId>
|
|
24
|
-
<version>0.6.0</version>
|
|
25
|
-
</dependency>
|
|
26
|
-
<dependency>
|
|
27
|
-
<groupId>org.springframework</groupId>
|
|
28
|
-
<artifactId>spring-context</artifactId>
|
|
29
|
-
<version>6.2.5</version>
|
|
30
|
-
<scope>provided</scope>
|
|
31
|
-
</dependency>
|
|
32
|
-
<dependency>
|
|
33
|
-
<groupId>org.springframework</groupId>
|
|
34
|
-
<artifactId>spring-jdbc</artifactId>
|
|
35
|
-
<version>6.2.5</version>
|
|
36
|
-
<scope>provided</scope>
|
|
37
|
-
</dependency>
|
|
38
|
-
<dependency>
|
|
39
|
-
<groupId>com.fasterxml.jackson.core</groupId>
|
|
40
|
-
<artifactId>jackson-databind</artifactId>
|
|
41
|
-
<version>2.18.2</version>
|
|
42
|
-
</dependency>
|
|
43
|
-
</dependencies>
|
|
44
|
-
|
|
45
|
-
<build>
|
|
46
|
-
<plugins>
|
|
47
|
-
<plugin>
|
|
48
|
-
<groupId>org.apache.maven.plugins</groupId>
|
|
49
|
-
<artifactId>maven-compiler-plugin</artifactId>
|
|
50
|
-
<version>3.14.0</version>
|
|
51
|
-
<configuration>
|
|
52
|
-
<release>${java.version}</release>
|
|
53
|
-
</configuration>
|
|
54
|
-
</plugin>
|
|
55
|
-
</plugins>
|
|
56
|
-
</build>
|
|
57
|
-
</project>
|
|
@@ -1,251 +0,0 @@
|
|
|
1
|
-
package app.portaki.module.sections;
|
|
2
|
-
|
|
3
|
-
import java.sql.ResultSet;
|
|
4
|
-
import java.sql.SQLException;
|
|
5
|
-
import java.time.Instant;
|
|
6
|
-
import java.util.HashMap;
|
|
7
|
-
import java.util.List;
|
|
8
|
-
import java.util.Map;
|
|
9
|
-
import java.util.UUID;
|
|
10
|
-
|
|
11
|
-
import org.springframework.jdbc.core.JdbcTemplate;
|
|
12
|
-
import org.springframework.stereotype.Component;
|
|
13
|
-
|
|
14
|
-
import com.fasterxml.jackson.core.JsonProcessingException;
|
|
15
|
-
import com.fasterxml.jackson.databind.JsonNode;
|
|
16
|
-
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
17
|
-
|
|
18
|
-
import app.portaki.sdk.gateway.GatewayModuleContext;
|
|
19
|
-
import app.portaki.sdk.gateway.PortakiCommandHandler;
|
|
20
|
-
import app.portaki.sdk.gateway.PortakiQueryHandler;
|
|
21
|
-
import app.portaki.sdk.module.PortakiModule;
|
|
22
|
-
|
|
23
|
-
@Component
|
|
24
|
-
@PortakiModule("sections")
|
|
25
|
-
public class SectionsGatewayModule {
|
|
26
|
-
|
|
27
|
-
private final JdbcTemplate jdbc;
|
|
28
|
-
private final ObjectMapper objectMapper;
|
|
29
|
-
|
|
30
|
-
public SectionsGatewayModule(JdbcTemplate jdbc, ObjectMapper objectMapper) {
|
|
31
|
-
this.jdbc = jdbc;
|
|
32
|
-
this.objectMapper = objectMapper;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
@PortakiQueryHandler(value = "sections.list", scope = "property:read")
|
|
36
|
-
public Map<String, Object> listSections(Map<String, Object> params, GatewayModuleContext ctx) {
|
|
37
|
-
UUID tenantId = ctx.tenantIdInternal();
|
|
38
|
-
UUID propertyId = UUID.fromString(ctx.propertyId());
|
|
39
|
-
List<Map<String, Object>> rows =
|
|
40
|
-
jdbc.query(
|
|
41
|
-
"""
|
|
42
|
-
SELECT id, sort_order, title_fr, title_en, content_fr, content_en, updated_at
|
|
43
|
-
FROM t_e_module_sections_item
|
|
44
|
-
WHERE tenant_id = ? AND property_id = ?
|
|
45
|
-
ORDER BY sort_order ASC, created_at ASC
|
|
46
|
-
""",
|
|
47
|
-
(rs, rowNum) -> toSectionRow(rs),
|
|
48
|
-
tenantId,
|
|
49
|
-
propertyId);
|
|
50
|
-
return Map.of("sections", rows);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
@PortakiCommandHandler(value = "sections.section.save", scope = "host:property:write")
|
|
54
|
-
public void saveSection(Map<String, Object> params, GatewayModuleContext ctx) {
|
|
55
|
-
UUID tenantId = ctx.tenantIdInternal();
|
|
56
|
-
UUID propertyId = UUID.fromString(ctx.propertyId());
|
|
57
|
-
String idRaw = stringParam(params, "id");
|
|
58
|
-
UUID id = idRaw == null || idRaw.isBlank() ? UUID.randomUUID() : UUID.fromString(idRaw);
|
|
59
|
-
String titleFr = stringParam(params, "titleFr");
|
|
60
|
-
String titleEn = stringParam(params, "titleEn");
|
|
61
|
-
JsonNode contentFr = readJsonParam(params, "contentFr");
|
|
62
|
-
JsonNode contentEn = readJsonParam(params, "contentEn");
|
|
63
|
-
int sortOrder = intParam(params, "sortOrder", nextSortOrder(tenantId, propertyId, id));
|
|
64
|
-
|
|
65
|
-
if (titleFr == null || titleFr.isBlank()) {
|
|
66
|
-
throw new IllegalArgumentException("title_fr_required");
|
|
67
|
-
}
|
|
68
|
-
if (titleEn == null || titleEn.isBlank()) {
|
|
69
|
-
titleEn = titleFr;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
boolean exists =
|
|
73
|
-
Boolean.TRUE.equals(
|
|
74
|
-
jdbc.queryForObject(
|
|
75
|
-
"""
|
|
76
|
-
SELECT EXISTS(
|
|
77
|
-
SELECT 1 FROM t_e_module_sections_item
|
|
78
|
-
WHERE id = ? AND tenant_id = ? AND property_id = ?
|
|
79
|
-
)
|
|
80
|
-
""",
|
|
81
|
-
Boolean.class,
|
|
82
|
-
id,
|
|
83
|
-
tenantId,
|
|
84
|
-
propertyId));
|
|
85
|
-
|
|
86
|
-
if (exists) {
|
|
87
|
-
jdbc.update(
|
|
88
|
-
"""
|
|
89
|
-
UPDATE t_e_module_sections_item
|
|
90
|
-
SET title_fr = ?, title_en = ?, content_fr = ?::jsonb, content_en = ?::jsonb,
|
|
91
|
-
sort_order = ?, updated_at = ?
|
|
92
|
-
WHERE id = ? AND tenant_id = ? AND property_id = ?
|
|
93
|
-
""",
|
|
94
|
-
titleFr,
|
|
95
|
-
titleEn,
|
|
96
|
-
jsonOrNull(contentFr),
|
|
97
|
-
jsonOrNull(contentEn),
|
|
98
|
-
sortOrder,
|
|
99
|
-
Instant.now(),
|
|
100
|
-
id,
|
|
101
|
-
tenantId,
|
|
102
|
-
propertyId);
|
|
103
|
-
} else {
|
|
104
|
-
jdbc.update(
|
|
105
|
-
"""
|
|
106
|
-
INSERT INTO t_e_module_sections_item (
|
|
107
|
-
id, tenant_id, property_id, sort_order, title_fr, title_en,
|
|
108
|
-
content_fr, content_en, created_at, updated_at
|
|
109
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?::jsonb, ?::jsonb, ?, ?)
|
|
110
|
-
""",
|
|
111
|
-
id,
|
|
112
|
-
tenantId,
|
|
113
|
-
propertyId,
|
|
114
|
-
sortOrder,
|
|
115
|
-
titleFr,
|
|
116
|
-
titleEn,
|
|
117
|
-
jsonOrNull(contentFr),
|
|
118
|
-
jsonOrNull(contentEn),
|
|
119
|
-
Instant.now(),
|
|
120
|
-
Instant.now());
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
@PortakiCommandHandler(value = "sections.section.delete", scope = "host:property:write")
|
|
126
|
-
public void deleteSection(Map<String, Object> params, GatewayModuleContext ctx) {
|
|
127
|
-
UUID tenantId = ctx.tenantIdInternal();
|
|
128
|
-
UUID propertyId = UUID.fromString(ctx.propertyId());
|
|
129
|
-
String idRaw = stringParam(params, "id");
|
|
130
|
-
if (idRaw == null || idRaw.isBlank()) {
|
|
131
|
-
throw new IllegalArgumentException("id_required");
|
|
132
|
-
}
|
|
133
|
-
UUID id = UUID.fromString(idRaw);
|
|
134
|
-
int deleted =
|
|
135
|
-
jdbc.update(
|
|
136
|
-
"""
|
|
137
|
-
DELETE FROM t_e_module_sections_item
|
|
138
|
-
WHERE id = ? AND tenant_id = ? AND property_id = ?
|
|
139
|
-
""",
|
|
140
|
-
id,
|
|
141
|
-
tenantId,
|
|
142
|
-
propertyId);
|
|
143
|
-
if (deleted == 0) {
|
|
144
|
-
throw new IllegalArgumentException("section_not_found");
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
@PortakiCommandHandler(value = "sections.reorder", scope = "host:property:write")
|
|
149
|
-
public void reorder(Map<String, Object> params, GatewayModuleContext ctx) {
|
|
150
|
-
UUID tenantId = ctx.tenantIdInternal();
|
|
151
|
-
UUID propertyId = UUID.fromString(ctx.propertyId());
|
|
152
|
-
Object raw = params.get("orderedIds");
|
|
153
|
-
if (!(raw instanceof List<?> list) || list.isEmpty()) {
|
|
154
|
-
throw new IllegalArgumentException("ordered_ids_required");
|
|
155
|
-
}
|
|
156
|
-
int order = 0;
|
|
157
|
-
for (Object item : list) {
|
|
158
|
-
if (item == null) {
|
|
159
|
-
continue;
|
|
160
|
-
}
|
|
161
|
-
UUID id = UUID.fromString(item.toString());
|
|
162
|
-
jdbc.update(
|
|
163
|
-
"""
|
|
164
|
-
UPDATE t_e_module_sections_item
|
|
165
|
-
SET sort_order = ?, updated_at = ?
|
|
166
|
-
WHERE id = ? AND tenant_id = ? AND property_id = ?
|
|
167
|
-
""",
|
|
168
|
-
order++,
|
|
169
|
-
Instant.now(),
|
|
170
|
-
id,
|
|
171
|
-
tenantId,
|
|
172
|
-
propertyId);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
private Map<String, Object> toSectionRow(ResultSet rs) throws SQLException {
|
|
177
|
-
Map<String, Object> row = new HashMap<>();
|
|
178
|
-
row.put("id", rs.getObject("id", UUID.class).toString());
|
|
179
|
-
row.put("sortOrder", rs.getInt("sort_order"));
|
|
180
|
-
row.put("titleFr", rs.getString("title_fr"));
|
|
181
|
-
row.put("titleEn", rs.getString("title_en"));
|
|
182
|
-
row.put("contentFr", parseJsonColumn(rs.getString("content_fr")));
|
|
183
|
-
row.put("contentEn", parseJsonColumn(rs.getString("content_en")));
|
|
184
|
-
Instant updatedAt = rs.getObject("updated_at", Instant.class);
|
|
185
|
-
if (updatedAt != null) {
|
|
186
|
-
row.put("updatedAt", updatedAt.toString());
|
|
187
|
-
}
|
|
188
|
-
return row;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
private Object parseJsonColumn(String raw) {
|
|
192
|
-
if (raw == null || raw.isBlank()) {
|
|
193
|
-
return null;
|
|
194
|
-
}
|
|
195
|
-
try {
|
|
196
|
-
return objectMapper.readValue(raw, Object.class);
|
|
197
|
-
} catch (JsonProcessingException e) {
|
|
198
|
-
return null;
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
private int nextSortOrder(UUID tenantId, UUID propertyId, UUID exceptId) {
|
|
203
|
-
Integer max =
|
|
204
|
-
jdbc.queryForObject(
|
|
205
|
-
"""
|
|
206
|
-
SELECT COALESCE(MAX(sort_order), -1)
|
|
207
|
-
FROM t_e_module_sections_item
|
|
208
|
-
WHERE tenant_id = ? AND property_id = ? AND id <> ?
|
|
209
|
-
""",
|
|
210
|
-
Integer.class,
|
|
211
|
-
tenantId,
|
|
212
|
-
propertyId,
|
|
213
|
-
exceptId);
|
|
214
|
-
return max == null ? 0 : max + 1;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
private static String stringParam(Map<String, Object> params, String key) {
|
|
218
|
-
Object v = params.get(key);
|
|
219
|
-
return v == null ? null : v.toString();
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
private static int intParam(Map<String, Object> params, String key, int defaultValue) {
|
|
223
|
-
Object v = params.get(key);
|
|
224
|
-
if (v == null) {
|
|
225
|
-
return defaultValue;
|
|
226
|
-
}
|
|
227
|
-
if (v instanceof Number n) {
|
|
228
|
-
return n.intValue();
|
|
229
|
-
}
|
|
230
|
-
return Integer.parseInt(v.toString());
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
private JsonNode readJsonParam(Map<String, Object> params, String key) {
|
|
234
|
-
Object v = params.get(key);
|
|
235
|
-
if (v == null) {
|
|
236
|
-
return null;
|
|
237
|
-
}
|
|
238
|
-
return objectMapper.valueToTree(v);
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
private String jsonOrNull(JsonNode node) {
|
|
242
|
-
if (node == null || node.isNull()) {
|
|
243
|
-
return null;
|
|
244
|
-
}
|
|
245
|
-
try {
|
|
246
|
-
return objectMapper.writeValueAsString(node);
|
|
247
|
-
} catch (JsonProcessingException e) {
|
|
248
|
-
throw new IllegalArgumentException("invalid_json", e);
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
}
|
package/backend/src/main/java/app/portaki/module/sections/SectionsModuleBackendConfiguration.java
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
package app.portaki.module.sections;
|
|
2
|
-
|
|
3
|
-
import org.springframework.context.annotation.ComponentScan;
|
|
4
|
-
import org.springframework.context.annotation.Configuration;
|
|
5
|
-
|
|
6
|
-
@Configuration
|
|
7
|
-
@ComponentScan(basePackageClasses = SectionsGatewayModule.class)
|
|
8
|
-
public class SectionsModuleBackendConfiguration {}
|
|
Binary file
|
package/backend/target/classes/app/portaki/module/sections/SectionsModuleBackendConfiguration.class
DELETED
|
Binary file
|
package/backend/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
/Users/cyrilcolinet/Documents/Projects/PortakiApp/Repositories/portaki-modules/modules/sections/backend/src/main/java/app/portaki/module/sections/SectionsGatewayModule.java
|
|
2
|
-
/Users/cyrilcolinet/Documents/Projects/PortakiApp/Repositories/portaki-modules/modules/sections/backend/src/main/java/app/portaki/module/sections/SectionsModuleBackendConfiguration.java
|
|
Binary file
|
package/db/README.md
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
# Schéma `sections`
|
|
2
|
-
|
|
3
|
-
- **Propriété** : module `sections` (`app.portaki.module.sections`).
|
|
4
|
-
- **Source de vérité** : `schema.sql` dans ce dossier.
|
|
5
|
-
- **Application aujourd’hui** : copie versionnée dans `portaki-api/.../db/migration/V58__module_sections_items.sql` (Flyway unique sur la base core).
|
|
6
|
-
- **Cible** : job **Atlas** / `portaki-module-migrator` (schéma Postgres dédié ou préfixe module) — voir `portaki-internal-docs/MODULE_PLATFORM_PREPARATION.md`.
|
|
7
|
-
|
|
8
|
-
Ne pas ajouter d’entités JPA pour ce module dans `portaki-api` : persistance dans `backend/` uniquement.
|
package/db/schema.sql
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
-- Canonical DDL for module `sections` (review in portaki-modules).
|
|
2
|
-
-- Runtime: copied to portaki-api Flyway until Atlas module migrator exists.
|
|
3
|
-
|
|
4
|
-
CREATE TABLE t_e_module_sections_item (
|
|
5
|
-
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
6
|
-
tenant_id UUID NOT NULL REFERENCES t_e_tenants (id),
|
|
7
|
-
property_id UUID NOT NULL REFERENCES t_e_properties (id) ON DELETE CASCADE,
|
|
8
|
-
sort_order INT NOT NULL DEFAULT 0,
|
|
9
|
-
title_fr TEXT NOT NULL DEFAULT '',
|
|
10
|
-
title_en TEXT NOT NULL DEFAULT '',
|
|
11
|
-
content_fr JSONB,
|
|
12
|
-
content_en JSONB,
|
|
13
|
-
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
14
|
-
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
15
|
-
);
|
|
16
|
-
|
|
17
|
-
CREATE INDEX t_e_module_sections_item_property_idx
|
|
18
|
-
ON t_e_module_sections_item (tenant_id, property_id);
|