@autocode-cli/autocode 0.1.4 → 0.1.6
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/README.md +28 -24
- package/dist/cli/commands/init.js +1 -1
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/serve.d.ts.map +1 -1
- package/dist/cli/commands/serve.js +27 -0
- package/dist/cli/commands/serve.js.map +1 -1
- package/dist/cli/commands/sync.d.ts +9 -0
- package/dist/cli/commands/sync.d.ts.map +1 -0
- package/dist/cli/commands/sync.js +91 -0
- package/dist/cli/commands/sync.js.map +1 -0
- package/dist/cli/parser.d.ts.map +1 -1
- package/dist/cli/parser.js +2 -0
- package/dist/cli/parser.js.map +1 -1
- package/dist/core/catalog.d.ts +52 -0
- package/dist/core/catalog.d.ts.map +1 -0
- package/dist/core/catalog.js +101 -0
- package/dist/core/catalog.js.map +1 -0
- package/dist/core/column.d.ts +2 -1
- package/dist/core/column.d.ts.map +1 -1
- package/dist/core/column.js +23 -11
- package/dist/core/column.js.map +1 -1
- package/dist/core/pipeline.d.ts +70 -0
- package/dist/core/pipeline.d.ts.map +1 -0
- package/dist/core/pipeline.js +199 -0
- package/dist/core/pipeline.js.map +1 -0
- package/dist/core/sync.d.ts +41 -0
- package/dist/core/sync.d.ts.map +1 -0
- package/dist/core/sync.js +269 -0
- package/dist/core/sync.js.map +1 -0
- package/dist/server/api.d.ts.map +1 -1
- package/dist/server/api.js +300 -0
- package/dist/server/api.js.map +1 -1
- package/dist/server/dashboard/pages/index.d.ts +1 -0
- package/dist/server/dashboard/pages/index.d.ts.map +1 -1
- package/dist/server/dashboard/pages/index.js +1 -0
- package/dist/server/dashboard/pages/index.js.map +1 -1
- package/dist/server/dashboard/pages/main-dashboard.d.ts.map +1 -1
- package/dist/server/dashboard/pages/main-dashboard.js +2 -1
- package/dist/server/dashboard/pages/main-dashboard.js.map +1 -1
- package/dist/server/dashboard/pages/pipeline-configurator.d.ts +8 -0
- package/dist/server/dashboard/pages/pipeline-configurator.d.ts.map +1 -0
- package/dist/server/dashboard/pages/pipeline-configurator.js +1531 -0
- package/dist/server/dashboard/pages/pipeline-configurator.js.map +1 -0
- package/dist/server/dashboard/pages/water-quality-form.d.ts +10 -0
- package/dist/server/dashboard/pages/water-quality-form.d.ts.map +1 -0
- package/dist/server/dashboard/pages/water-quality-form.js +910 -0
- package/dist/server/dashboard/pages/water-quality-form.js.map +1 -0
- package/dist/server/dashboard/styles/base.d.ts.map +1 -1
- package/dist/server/dashboard/styles/base.js +11 -0
- package/dist/server/dashboard/styles/base.js.map +1 -1
- package/dist/server/dashboard.d.ts +1 -1
- package/dist/server/dashboard.d.ts.map +1 -1
- package/dist/server/dashboard.js +1 -1
- package/dist/server/dashboard.js.map +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +67 -1
- package/dist/server/index.js.map +1 -1
- package/dist/services/claude.d.ts +2 -1
- package/dist/services/claude.d.ts.map +1 -1
- package/dist/services/claude.js +8 -1
- package/dist/services/claude.js.map +1 -1
- package/dist/types/index.d.ts +67 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/config.d.ts +28 -20
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +12 -10
- package/dist/utils/config.js.map +1 -1
- package/package.json +2 -2
- package/templates/catalog.yaml +100 -0
- package/templates/{columns/00_backlog.en.md → prompts/backlog.en.md} +1 -1
- package/templates/{columns/00_backlog.fr.md → prompts/backlog.fr.md} +1 -1
- package/templates/prompts/changelog.en.md +31 -0
- package/templates/prompts/changelog.fr.md +31 -0
- package/templates/prompts/deploy-prod.en.md +32 -0
- package/templates/prompts/deploy-prod.fr.md +32 -0
- package/templates/{columns/10_deploy-staging.en.md → prompts/deploy-staging.en.md} +1 -1
- package/templates/{columns/10_deploy-staging.fr.md → prompts/deploy-staging.fr.md} +1 -1
- package/templates/prompts/design.en.md +30 -0
- package/templates/prompts/design.fr.md +30 -0
- package/templates/prompts/dev.en.md +31 -0
- package/templates/prompts/dev.fr.md +31 -0
- package/templates/{columns/12_done.en.md → prompts/done.en.md} +1 -1
- package/templates/{columns/12_done.fr.md → prompts/done.fr.md} +1 -1
- package/templates/prompts/git-commit.en.md +35 -0
- package/templates/prompts/git-commit.fr.md +35 -0
- package/templates/prompts/git-push.en.md +31 -0
- package/templates/prompts/git-push.fr.md +31 -0
- package/templates/prompts/git-tag.en.md +32 -0
- package/templates/prompts/git-tag.fr.md +32 -0
- package/templates/{columns/02_in-progress.en.md → prompts/in-progress.en.md} +1 -1
- package/templates/{columns/02_in-progress.fr.md → prompts/in-progress.fr.md} +1 -1
- package/templates/prompts/qualification.en.md +30 -0
- package/templates/prompts/qualification.fr.md +30 -0
- package/templates/{columns/01_ready.en.md → prompts/ready.en.md} +1 -1
- package/templates/{columns/01_ready.fr.md → prompts/ready.fr.md} +1 -1
- package/templates/prompts/retest-cypress.en.md +29 -0
- package/templates/prompts/retest-cypress.fr.md +29 -0
- package/templates/prompts/retest-playwright.en.md +30 -0
- package/templates/prompts/retest-playwright.fr.md +30 -0
- package/templates/prompts/retest.en.md +31 -0
- package/templates/prompts/retest.fr.md +31 -0
- package/templates/{columns/03_review-best-practices.en.md → prompts/review-best-practices.en.md} +1 -1
- package/templates/{columns/03_review-best-practices.fr.md → prompts/review-best-practices.fr.md} +1 -1
- package/templates/prompts/review-code.en.md +31 -0
- package/templates/prompts/review-code.fr.md +31 -0
- package/templates/{columns/05_review-consistency.en.md → prompts/review-consistency.en.md} +1 -1
- package/templates/{columns/05_review-consistency.fr.md → prompts/review-consistency.fr.md} +1 -1
- package/templates/{columns/04_review-no-duplication.en.md → prompts/review-no-duplication.en.md} +1 -1
- package/templates/{columns/04_review-no-duplication.fr.md → prompts/review-no-duplication.fr.md} +1 -1
- package/templates/{columns/06_review-security.en.md → prompts/review-security.en.md} +1 -1
- package/templates/{columns/06_review-security.fr.md → prompts/review-security.fr.md} +1 -1
- package/templates/prompts/specification.en.md +30 -0
- package/templates/prompts/specification.fr.md +30 -0
- package/templates/{columns/08_testing-cypress.en.md → prompts/testing-cypress.en.md} +1 -1
- package/templates/{columns/08_testing-cypress.fr.md → prompts/testing-cypress.fr.md} +1 -1
- package/templates/prompts/testing-integration.en.md +32 -0
- package/templates/prompts/testing-integration.fr.md +32 -0
- package/templates/{columns/07_testing-playwright.en.md → prompts/testing-playwright.en.md} +1 -1
- package/templates/{columns/07_testing-playwright.fr.md → prompts/testing-playwright.fr.md} +1 -1
- package/templates/prompts/testing-unit.en.md +32 -0
- package/templates/prompts/testing-unit.fr.md +32 -0
- package/templates/{columns/09_update-docs.en.md → prompts/update-docs.en.md} +1 -1
- package/templates/{columns/09_update-docs.fr.md → prompts/update-docs.fr.md} +1 -1
- package/templates/{columns/11_validate-staging.en.md → prompts/validate-staging.en.md} +1 -1
- package/templates/{columns/11_validate-staging.fr.md → prompts/validate-staging.fr.md} +1 -1
|
@@ -0,0 +1,910 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Water Quality Form Page
|
|
3
|
+
*
|
|
4
|
+
* Form for recording seawater quality measurements.
|
|
5
|
+
*/
|
|
6
|
+
import { getVariables } from '../styles/variables.js';
|
|
7
|
+
import { getBaseStyles } from '../styles/base.js';
|
|
8
|
+
import { getComponentStyles } from '../styles/components.js';
|
|
9
|
+
/**
|
|
10
|
+
* Get water quality form specific styles
|
|
11
|
+
* These extend the base styles with form-specific styling
|
|
12
|
+
*/
|
|
13
|
+
function getWaterQualityFormStyles() {
|
|
14
|
+
return `
|
|
15
|
+
main {
|
|
16
|
+
max-width: 1200px;
|
|
17
|
+
margin: 0 auto;
|
|
18
|
+
padding: 24px;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.form-container {
|
|
22
|
+
background: var(--bg-secondary);
|
|
23
|
+
border-radius: 12px;
|
|
24
|
+
padding: 32px;
|
|
25
|
+
border: 1px solid var(--border);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.section {
|
|
29
|
+
margin-bottom: 32px;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.section-title {
|
|
33
|
+
font-size: 1.1rem;
|
|
34
|
+
font-weight: 600;
|
|
35
|
+
color: var(--accent);
|
|
36
|
+
margin-bottom: 20px;
|
|
37
|
+
padding-bottom: 10px;
|
|
38
|
+
border-bottom: 1px solid var(--border);
|
|
39
|
+
display: flex;
|
|
40
|
+
align-items: center;
|
|
41
|
+
gap: 10px;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.section-title::before {
|
|
45
|
+
content: '';
|
|
46
|
+
width: 4px;
|
|
47
|
+
height: 20px;
|
|
48
|
+
background: var(--accent);
|
|
49
|
+
border-radius: 2px;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.form-grid {
|
|
53
|
+
display: grid;
|
|
54
|
+
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
|
55
|
+
gap: 20px;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.form-group.full-width {
|
|
59
|
+
grid-column: 1 / -1;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
label .required {
|
|
63
|
+
color: var(--red);
|
|
64
|
+
margin-left: 4px;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
label .unit {
|
|
68
|
+
color: var(--text-secondary);
|
|
69
|
+
font-weight: normal;
|
|
70
|
+
margin-left: 4px;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
input[type="number"] {
|
|
74
|
+
-moz-appearance: textfield;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
input[type="number"]::-webkit-outer-spin-button,
|
|
78
|
+
input[type="number"]::-webkit-inner-spin-button {
|
|
79
|
+
-webkit-appearance: none;
|
|
80
|
+
margin: 0;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.input-with-unit {
|
|
84
|
+
position: relative;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.input-with-unit input {
|
|
88
|
+
width: 100%;
|
|
89
|
+
padding-right: 60px;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.input-with-unit .unit-label {
|
|
93
|
+
position: absolute;
|
|
94
|
+
right: 12px;
|
|
95
|
+
top: 50%;
|
|
96
|
+
transform: translateY(-50%);
|
|
97
|
+
color: var(--muted);
|
|
98
|
+
font-size: 13px;
|
|
99
|
+
pointer-events: none;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.gps-inputs {
|
|
103
|
+
display: grid;
|
|
104
|
+
grid-template-columns: 1fr 1fr;
|
|
105
|
+
gap: 12px;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.form-actions {
|
|
109
|
+
display: flex;
|
|
110
|
+
gap: 16px;
|
|
111
|
+
justify-content: flex-end;
|
|
112
|
+
margin-top: 32px;
|
|
113
|
+
padding-top: 24px;
|
|
114
|
+
border-top: 1px solid var(--border);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.btn:disabled {
|
|
118
|
+
opacity: 0.6;
|
|
119
|
+
cursor: not-allowed;
|
|
120
|
+
transform: none;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.notification {
|
|
124
|
+
position: fixed;
|
|
125
|
+
top: 20px;
|
|
126
|
+
right: 20px;
|
|
127
|
+
padding: 16px 24px;
|
|
128
|
+
border-radius: 8px;
|
|
129
|
+
font-size: 14px;
|
|
130
|
+
font-weight: 500;
|
|
131
|
+
z-index: 1000;
|
|
132
|
+
animation: slideIn 0.3s ease;
|
|
133
|
+
display: none;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.notification.success {
|
|
137
|
+
background: var(--green);
|
|
138
|
+
color: #fff;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.notification.error {
|
|
142
|
+
background: var(--red);
|
|
143
|
+
color: #fff;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.notification.show {
|
|
147
|
+
display: block;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
@keyframes slideIn {
|
|
151
|
+
from {
|
|
152
|
+
transform: translateX(100%);
|
|
153
|
+
opacity: 0;
|
|
154
|
+
}
|
|
155
|
+
to {
|
|
156
|
+
transform: translateX(0);
|
|
157
|
+
opacity: 1;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.quality-indicator {
|
|
162
|
+
display: flex;
|
|
163
|
+
align-items: center;
|
|
164
|
+
gap: 12px;
|
|
165
|
+
padding: 16px;
|
|
166
|
+
background: var(--bg-tertiary);
|
|
167
|
+
border-radius: 8px;
|
|
168
|
+
margin-top: 20px;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.quality-badge {
|
|
172
|
+
padding: 6px 16px;
|
|
173
|
+
border-radius: 20px;
|
|
174
|
+
font-size: 13px;
|
|
175
|
+
font-weight: 600;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.quality-good { background: var(--green); color: #fff; }
|
|
179
|
+
.quality-moderate { background: var(--yellow); color: #000; }
|
|
180
|
+
.quality-poor { background: #f97316; color: #fff; }
|
|
181
|
+
.quality-critical { background: var(--red); color: #fff; }
|
|
182
|
+
|
|
183
|
+
footer {
|
|
184
|
+
text-align: center;
|
|
185
|
+
padding: 20px;
|
|
186
|
+
color: var(--muted);
|
|
187
|
+
font-size: 12px;
|
|
188
|
+
border-top: 1px solid var(--border);
|
|
189
|
+
margin-top: 40px;
|
|
190
|
+
}
|
|
191
|
+
`;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Generate the water quality form page
|
|
195
|
+
*/
|
|
196
|
+
export function generateWaterQualityFormPage(lang = 'fr') {
|
|
197
|
+
const translations = {
|
|
198
|
+
en: {
|
|
199
|
+
title: 'Seawater Quality Form',
|
|
200
|
+
subtitle: 'Record water quality measurements',
|
|
201
|
+
sampleInfo: 'Sample Information',
|
|
202
|
+
sampleId: 'Sample ID',
|
|
203
|
+
sampleIdPlaceholder: 'e.g., SW-2025-001',
|
|
204
|
+
sampleDate: 'Sampling Date',
|
|
205
|
+
sampleTime: 'Sampling Time',
|
|
206
|
+
location: 'Sampling Location',
|
|
207
|
+
locationPlaceholder: 'e.g., Port of Marseille - Dock 3',
|
|
208
|
+
gpsCoordinates: 'GPS Coordinates',
|
|
209
|
+
latitude: 'Latitude',
|
|
210
|
+
longitude: 'Longitude',
|
|
211
|
+
depth: 'Sampling Depth (m)',
|
|
212
|
+
physicalParams: 'Physical Parameters',
|
|
213
|
+
temperature: 'Temperature',
|
|
214
|
+
temperatureUnit: 'Celsius',
|
|
215
|
+
salinity: 'Salinity',
|
|
216
|
+
salinityUnit: 'PSU',
|
|
217
|
+
turbidity: 'Turbidity',
|
|
218
|
+
turbidityUnit: 'NTU',
|
|
219
|
+
dissolvedOxygen: 'Dissolved Oxygen',
|
|
220
|
+
dissolvedOxygenUnit: 'mg/L',
|
|
221
|
+
ph: 'pH',
|
|
222
|
+
conductivity: 'Conductivity',
|
|
223
|
+
conductivityUnit: 'mS/cm',
|
|
224
|
+
chemicalParams: 'Chemical Parameters',
|
|
225
|
+
nitrates: 'Nitrates (NO3)',
|
|
226
|
+
nitratesUnit: 'mg/L',
|
|
227
|
+
nitrites: 'Nitrites (NO2)',
|
|
228
|
+
nitritesUnit: 'mg/L',
|
|
229
|
+
phosphates: 'Phosphates (PO4)',
|
|
230
|
+
phosphatesUnit: 'mg/L',
|
|
231
|
+
ammonia: 'Ammonia (NH3)',
|
|
232
|
+
ammoniaUnit: 'mg/L',
|
|
233
|
+
chlorophyll: 'Chlorophyll-a',
|
|
234
|
+
chlorophyllUnit: 'µg/L',
|
|
235
|
+
biologicalParams: 'Biological Parameters',
|
|
236
|
+
coliformBacteria: 'Coliform Bacteria',
|
|
237
|
+
coliformUnit: 'CFU/100mL',
|
|
238
|
+
enterococci: 'Enterococci',
|
|
239
|
+
enterococciUnit: 'CFU/100mL',
|
|
240
|
+
phytoplankton: 'Phytoplankton Density',
|
|
241
|
+
phytoplanktonUnit: 'cells/mL',
|
|
242
|
+
visualObservations: 'Visual Observations',
|
|
243
|
+
waterColor: 'Water Color',
|
|
244
|
+
colorOptions: {
|
|
245
|
+
clear: 'Clear',
|
|
246
|
+
greenish: 'Greenish',
|
|
247
|
+
brownish: 'Brownish',
|
|
248
|
+
milky: 'Milky',
|
|
249
|
+
other: 'Other'
|
|
250
|
+
},
|
|
251
|
+
odor: 'Odor',
|
|
252
|
+
odorOptions: {
|
|
253
|
+
none: 'None',
|
|
254
|
+
marine: 'Normal Marine',
|
|
255
|
+
sulfurous: 'Sulfurous',
|
|
256
|
+
chemical: 'Chemical',
|
|
257
|
+
other: 'Other'
|
|
258
|
+
},
|
|
259
|
+
floatingDebris: 'Floating Debris',
|
|
260
|
+
debrisOptions: {
|
|
261
|
+
none: 'None',
|
|
262
|
+
minimal: 'Minimal',
|
|
263
|
+
moderate: 'Moderate',
|
|
264
|
+
significant: 'Significant'
|
|
265
|
+
},
|
|
266
|
+
oilPresence: 'Oil/Hydrocarbon Presence',
|
|
267
|
+
oilOptions: {
|
|
268
|
+
none: 'None',
|
|
269
|
+
sheen: 'Light Sheen',
|
|
270
|
+
slick: 'Oil Slick',
|
|
271
|
+
heavy: 'Heavy Contamination'
|
|
272
|
+
},
|
|
273
|
+
weather: 'Weather Conditions',
|
|
274
|
+
weatherConditions: 'Current Weather',
|
|
275
|
+
weatherOptions: {
|
|
276
|
+
sunny: 'Sunny',
|
|
277
|
+
cloudy: 'Cloudy',
|
|
278
|
+
rainy: 'Rainy',
|
|
279
|
+
stormy: 'Stormy'
|
|
280
|
+
},
|
|
281
|
+
windSpeed: 'Wind Speed',
|
|
282
|
+
windSpeedUnit: 'km/h',
|
|
283
|
+
waveHeight: 'Wave Height',
|
|
284
|
+
waveHeightUnit: 'meters',
|
|
285
|
+
airTemperature: 'Air Temperature',
|
|
286
|
+
notes: 'Additional Notes',
|
|
287
|
+
notesPlaceholder: 'Any additional observations or comments...',
|
|
288
|
+
sampledBy: 'Sampled By',
|
|
289
|
+
sampledByPlaceholder: 'Technician name',
|
|
290
|
+
submit: 'Submit',
|
|
291
|
+
reset: 'Reset Form',
|
|
292
|
+
saving: 'Saving...',
|
|
293
|
+
success: 'Data saved successfully!',
|
|
294
|
+
error: 'Error saving data',
|
|
295
|
+
required: 'Required',
|
|
296
|
+
backToDashboard: 'Back to Dashboard',
|
|
297
|
+
qualityIndex: 'Water Quality Index',
|
|
298
|
+
qualityGood: 'Good',
|
|
299
|
+
qualityModerate: 'Moderate',
|
|
300
|
+
qualityPoor: 'Poor',
|
|
301
|
+
qualityCritical: 'Critical'
|
|
302
|
+
},
|
|
303
|
+
fr: {
|
|
304
|
+
title: 'Formulaire Qualité Eau de Mer',
|
|
305
|
+
subtitle: 'Enregistrement des mesures de qualité de l\'eau',
|
|
306
|
+
sampleInfo: 'Informations de l\'Échantillon',
|
|
307
|
+
sampleId: 'ID Échantillon',
|
|
308
|
+
sampleIdPlaceholder: 'ex: EM-2025-001',
|
|
309
|
+
sampleDate: 'Date de Prélèvement',
|
|
310
|
+
sampleTime: 'Heure de Prélèvement',
|
|
311
|
+
location: 'Lieu de Prélèvement',
|
|
312
|
+
locationPlaceholder: 'ex: Port de Marseille - Quai 3',
|
|
313
|
+
gpsCoordinates: 'Coordonnées GPS',
|
|
314
|
+
latitude: 'Latitude',
|
|
315
|
+
longitude: 'Longitude',
|
|
316
|
+
depth: 'Profondeur de Prélèvement (m)',
|
|
317
|
+
physicalParams: 'Paramètres Physiques',
|
|
318
|
+
temperature: 'Température',
|
|
319
|
+
temperatureUnit: 'Celsius',
|
|
320
|
+
salinity: 'Salinité',
|
|
321
|
+
salinityUnit: 'PSU',
|
|
322
|
+
turbidity: 'Turbidité',
|
|
323
|
+
turbidityUnit: 'NTU',
|
|
324
|
+
dissolvedOxygen: 'Oxygène Dissous',
|
|
325
|
+
dissolvedOxygenUnit: 'mg/L',
|
|
326
|
+
ph: 'pH',
|
|
327
|
+
conductivity: 'Conductivité',
|
|
328
|
+
conductivityUnit: 'mS/cm',
|
|
329
|
+
chemicalParams: 'Paramètres Chimiques',
|
|
330
|
+
nitrates: 'Nitrates (NO3)',
|
|
331
|
+
nitratesUnit: 'mg/L',
|
|
332
|
+
nitrites: 'Nitrites (NO2)',
|
|
333
|
+
nitritesUnit: 'mg/L',
|
|
334
|
+
phosphates: 'Phosphates (PO4)',
|
|
335
|
+
phosphatesUnit: 'mg/L',
|
|
336
|
+
ammonia: 'Ammoniac (NH3)',
|
|
337
|
+
ammoniaUnit: 'mg/L',
|
|
338
|
+
chlorophyll: 'Chlorophylle-a',
|
|
339
|
+
chlorophyllUnit: 'µg/L',
|
|
340
|
+
biologicalParams: 'Paramètres Biologiques',
|
|
341
|
+
coliformBacteria: 'Bactéries Coliformes',
|
|
342
|
+
coliformUnit: 'UFC/100mL',
|
|
343
|
+
enterococci: 'Entérocoques',
|
|
344
|
+
enterococciUnit: 'UFC/100mL',
|
|
345
|
+
phytoplankton: 'Densité Phytoplancton',
|
|
346
|
+
phytoplanktonUnit: 'cellules/mL',
|
|
347
|
+
visualObservations: 'Observations Visuelles',
|
|
348
|
+
waterColor: 'Couleur de l\'Eau',
|
|
349
|
+
colorOptions: {
|
|
350
|
+
clear: 'Claire',
|
|
351
|
+
greenish: 'Verdâtre',
|
|
352
|
+
brownish: 'Brunâtre',
|
|
353
|
+
milky: 'Laiteuse',
|
|
354
|
+
other: 'Autre'
|
|
355
|
+
},
|
|
356
|
+
odor: 'Odeur',
|
|
357
|
+
odorOptions: {
|
|
358
|
+
none: 'Aucune',
|
|
359
|
+
marine: 'Marine Normale',
|
|
360
|
+
sulfurous: 'Soufrée',
|
|
361
|
+
chemical: 'Chimique',
|
|
362
|
+
other: 'Autre'
|
|
363
|
+
},
|
|
364
|
+
floatingDebris: 'Débris Flottants',
|
|
365
|
+
debrisOptions: {
|
|
366
|
+
none: 'Aucun',
|
|
367
|
+
minimal: 'Minimal',
|
|
368
|
+
moderate: 'Modéré',
|
|
369
|
+
significant: 'Important'
|
|
370
|
+
},
|
|
371
|
+
oilPresence: 'Présence Huile/Hydrocarbures',
|
|
372
|
+
oilOptions: {
|
|
373
|
+
none: 'Aucune',
|
|
374
|
+
sheen: 'Irisation Légère',
|
|
375
|
+
slick: 'Nappe d\'Huile',
|
|
376
|
+
heavy: 'Contamination Importante'
|
|
377
|
+
},
|
|
378
|
+
weather: 'Conditions Météorologiques',
|
|
379
|
+
weatherConditions: 'Météo Actuelle',
|
|
380
|
+
weatherOptions: {
|
|
381
|
+
sunny: 'Ensoleillé',
|
|
382
|
+
cloudy: 'Nuageux',
|
|
383
|
+
rainy: 'Pluvieux',
|
|
384
|
+
stormy: 'Orageux'
|
|
385
|
+
},
|
|
386
|
+
windSpeed: 'Vitesse du Vent',
|
|
387
|
+
windSpeedUnit: 'km/h',
|
|
388
|
+
waveHeight: 'Hauteur des Vagues',
|
|
389
|
+
waveHeightUnit: 'mètres',
|
|
390
|
+
airTemperature: 'Température de l\'Air',
|
|
391
|
+
notes: 'Notes Supplémentaires',
|
|
392
|
+
notesPlaceholder: 'Observations ou commentaires additionnels...',
|
|
393
|
+
sampledBy: 'Prélevé par',
|
|
394
|
+
sampledByPlaceholder: 'Nom du technicien',
|
|
395
|
+
submit: 'Soumettre',
|
|
396
|
+
reset: 'Réinitialiser',
|
|
397
|
+
saving: 'Enregistrement...',
|
|
398
|
+
success: 'Données enregistrées avec succès!',
|
|
399
|
+
error: 'Erreur lors de l\'enregistrement',
|
|
400
|
+
required: 'Requis',
|
|
401
|
+
backToDashboard: 'Retour au Dashboard',
|
|
402
|
+
qualityIndex: 'Indice de Qualité',
|
|
403
|
+
qualityGood: 'Bonne',
|
|
404
|
+
qualityModerate: 'Modérée',
|
|
405
|
+
qualityPoor: 'Mauvaise',
|
|
406
|
+
qualityCritical: 'Critique'
|
|
407
|
+
}
|
|
408
|
+
};
|
|
409
|
+
const t = translations[lang] || translations.fr;
|
|
410
|
+
return `<!DOCTYPE html>
|
|
411
|
+
<html lang="${lang}">
|
|
412
|
+
<head>
|
|
413
|
+
<meta charset="UTF-8">
|
|
414
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
415
|
+
<title>${t.title} - AutoCode</title>
|
|
416
|
+
<style>
|
|
417
|
+
${getVariables()}
|
|
418
|
+
${getBaseStyles()}
|
|
419
|
+
${getComponentStyles()}
|
|
420
|
+
${getWaterQualityFormStyles()}
|
|
421
|
+
|
|
422
|
+
/* Page-specific header overrides */
|
|
423
|
+
.header-left {
|
|
424
|
+
display: flex;
|
|
425
|
+
align-items: center;
|
|
426
|
+
gap: 16px;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
.header-left a {
|
|
430
|
+
color: var(--accent);
|
|
431
|
+
text-decoration: none;
|
|
432
|
+
font-size: 14px;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
.header-left a:hover {
|
|
436
|
+
text-decoration: underline;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
h1 {
|
|
440
|
+
font-size: 1.5rem;
|
|
441
|
+
background: var(--gradient-1);
|
|
442
|
+
-webkit-background-clip: text;
|
|
443
|
+
-webkit-text-fill-color: transparent;
|
|
444
|
+
background-clip: text;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
.subtitle {
|
|
448
|
+
color: var(--muted);
|
|
449
|
+
font-size: 14px;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/* Form input enhancements */
|
|
453
|
+
input, select, textarea {
|
|
454
|
+
padding: 12px 14px;
|
|
455
|
+
font-size: 14px;
|
|
456
|
+
background: var(--bg-tertiary);
|
|
457
|
+
transition: border-color 0.2s, box-shadow 0.2s;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
input:focus, select:focus, textarea:focus {
|
|
461
|
+
box-shadow: 0 0 0 3px var(--accent-glow);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
input::placeholder, textarea::placeholder {
|
|
465
|
+
color: var(--muted);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
select {
|
|
469
|
+
cursor: pointer;
|
|
470
|
+
}
|
|
471
|
+
</style>
|
|
472
|
+
</head>
|
|
473
|
+
<body>
|
|
474
|
+
<div class="notification" id="notification"></div>
|
|
475
|
+
|
|
476
|
+
<header>
|
|
477
|
+
<div class="header-left">
|
|
478
|
+
<a href="/">← ${t.backToDashboard}</a>
|
|
479
|
+
<div>
|
|
480
|
+
<h1>${t.title}</h1>
|
|
481
|
+
<div class="subtitle">${t.subtitle}</div>
|
|
482
|
+
</div>
|
|
483
|
+
</div>
|
|
484
|
+
<div class="lang-switcher">
|
|
485
|
+
<button class="lang-btn ${lang === 'en' ? 'active' : ''}" onclick="switchLang('en')">EN</button>
|
|
486
|
+
<button class="lang-btn ${lang === 'fr' ? 'active' : ''}" onclick="switchLang('fr')">FR</button>
|
|
487
|
+
</div>
|
|
488
|
+
</header>
|
|
489
|
+
|
|
490
|
+
<main>
|
|
491
|
+
<form class="form-container" id="water-quality-form">
|
|
492
|
+
<!-- Sample Information -->
|
|
493
|
+
<div class="section">
|
|
494
|
+
<h2 class="section-title">${t.sampleInfo}</h2>
|
|
495
|
+
<div class="form-grid">
|
|
496
|
+
<div class="form-group">
|
|
497
|
+
<label for="sample-id">${t.sampleId}<span class="required">*</span></label>
|
|
498
|
+
<input type="text" id="sample-id" name="sampleId" required placeholder="${t.sampleIdPlaceholder}">
|
|
499
|
+
</div>
|
|
500
|
+
<div class="form-group">
|
|
501
|
+
<label for="sample-date">${t.sampleDate}<span class="required">*</span></label>
|
|
502
|
+
<input type="date" id="sample-date" name="sampleDate" required>
|
|
503
|
+
</div>
|
|
504
|
+
<div class="form-group">
|
|
505
|
+
<label for="sample-time">${t.sampleTime}<span class="required">*</span></label>
|
|
506
|
+
<input type="time" id="sample-time" name="sampleTime" required>
|
|
507
|
+
</div>
|
|
508
|
+
<div class="form-group">
|
|
509
|
+
<label for="sampled-by">${t.sampledBy}<span class="required">*</span></label>
|
|
510
|
+
<input type="text" id="sampled-by" name="sampledBy" required placeholder="${t.sampledByPlaceholder}">
|
|
511
|
+
</div>
|
|
512
|
+
<div class="form-group full-width">
|
|
513
|
+
<label for="location">${t.location}<span class="required">*</span></label>
|
|
514
|
+
<input type="text" id="location" name="location" required placeholder="${t.locationPlaceholder}">
|
|
515
|
+
</div>
|
|
516
|
+
<div class="form-group">
|
|
517
|
+
<label>${t.gpsCoordinates}</label>
|
|
518
|
+
<div class="gps-inputs">
|
|
519
|
+
<input type="number" step="0.000001" name="latitude" placeholder="${t.latitude}">
|
|
520
|
+
<input type="number" step="0.000001" name="longitude" placeholder="${t.longitude}">
|
|
521
|
+
</div>
|
|
522
|
+
</div>
|
|
523
|
+
<div class="form-group">
|
|
524
|
+
<label for="depth">${t.depth}</label>
|
|
525
|
+
<div class="input-with-unit">
|
|
526
|
+
<input type="number" step="0.1" id="depth" name="depth" min="0">
|
|
527
|
+
<span class="unit-label">m</span>
|
|
528
|
+
</div>
|
|
529
|
+
</div>
|
|
530
|
+
</div>
|
|
531
|
+
</div>
|
|
532
|
+
|
|
533
|
+
<!-- Physical Parameters -->
|
|
534
|
+
<div class="section">
|
|
535
|
+
<h2 class="section-title">${t.physicalParams}</h2>
|
|
536
|
+
<div class="form-grid">
|
|
537
|
+
<div class="form-group">
|
|
538
|
+
<label for="temperature">${t.temperature}<span class="unit">(${t.temperatureUnit})</span></label>
|
|
539
|
+
<div class="input-with-unit">
|
|
540
|
+
<input type="number" step="0.1" id="temperature" name="temperature">
|
|
541
|
+
<span class="unit-label">°C</span>
|
|
542
|
+
</div>
|
|
543
|
+
</div>
|
|
544
|
+
<div class="form-group">
|
|
545
|
+
<label for="salinity">${t.salinity}<span class="unit">(${t.salinityUnit})</span></label>
|
|
546
|
+
<div class="input-with-unit">
|
|
547
|
+
<input type="number" step="0.1" id="salinity" name="salinity">
|
|
548
|
+
<span class="unit-label">PSU</span>
|
|
549
|
+
</div>
|
|
550
|
+
</div>
|
|
551
|
+
<div class="form-group">
|
|
552
|
+
<label for="ph">${t.ph}</label>
|
|
553
|
+
<input type="number" step="0.01" id="ph" name="ph" min="0" max="14">
|
|
554
|
+
</div>
|
|
555
|
+
<div class="form-group">
|
|
556
|
+
<label for="dissolved-oxygen">${t.dissolvedOxygen}<span class="unit">(${t.dissolvedOxygenUnit})</span></label>
|
|
557
|
+
<div class="input-with-unit">
|
|
558
|
+
<input type="number" step="0.1" id="dissolved-oxygen" name="dissolvedOxygen">
|
|
559
|
+
<span class="unit-label">mg/L</span>
|
|
560
|
+
</div>
|
|
561
|
+
</div>
|
|
562
|
+
<div class="form-group">
|
|
563
|
+
<label for="turbidity">${t.turbidity}<span class="unit">(${t.turbidityUnit})</span></label>
|
|
564
|
+
<div class="input-with-unit">
|
|
565
|
+
<input type="number" step="0.1" id="turbidity" name="turbidity">
|
|
566
|
+
<span class="unit-label">NTU</span>
|
|
567
|
+
</div>
|
|
568
|
+
</div>
|
|
569
|
+
<div class="form-group">
|
|
570
|
+
<label for="conductivity">${t.conductivity}<span class="unit">(${t.conductivityUnit})</span></label>
|
|
571
|
+
<div class="input-with-unit">
|
|
572
|
+
<input type="number" step="0.01" id="conductivity" name="conductivity">
|
|
573
|
+
<span class="unit-label">mS/cm</span>
|
|
574
|
+
</div>
|
|
575
|
+
</div>
|
|
576
|
+
</div>
|
|
577
|
+
</div>
|
|
578
|
+
|
|
579
|
+
<!-- Chemical Parameters -->
|
|
580
|
+
<div class="section">
|
|
581
|
+
<h2 class="section-title">${t.chemicalParams}</h2>
|
|
582
|
+
<div class="form-grid">
|
|
583
|
+
<div class="form-group">
|
|
584
|
+
<label for="nitrates">${t.nitrates}<span class="unit">(${t.nitratesUnit})</span></label>
|
|
585
|
+
<div class="input-with-unit">
|
|
586
|
+
<input type="number" step="0.01" id="nitrates" name="nitrates">
|
|
587
|
+
<span class="unit-label">mg/L</span>
|
|
588
|
+
</div>
|
|
589
|
+
</div>
|
|
590
|
+
<div class="form-group">
|
|
591
|
+
<label for="nitrites">${t.nitrites}<span class="unit">(${t.nitritesUnit})</span></label>
|
|
592
|
+
<div class="input-with-unit">
|
|
593
|
+
<input type="number" step="0.001" id="nitrites" name="nitrites">
|
|
594
|
+
<span class="unit-label">mg/L</span>
|
|
595
|
+
</div>
|
|
596
|
+
</div>
|
|
597
|
+
<div class="form-group">
|
|
598
|
+
<label for="phosphates">${t.phosphates}<span class="unit">(${t.phosphatesUnit})</span></label>
|
|
599
|
+
<div class="input-with-unit">
|
|
600
|
+
<input type="number" step="0.01" id="phosphates" name="phosphates">
|
|
601
|
+
<span class="unit-label">mg/L</span>
|
|
602
|
+
</div>
|
|
603
|
+
</div>
|
|
604
|
+
<div class="form-group">
|
|
605
|
+
<label for="ammonia">${t.ammonia}<span class="unit">(${t.ammoniaUnit})</span></label>
|
|
606
|
+
<div class="input-with-unit">
|
|
607
|
+
<input type="number" step="0.01" id="ammonia" name="ammonia">
|
|
608
|
+
<span class="unit-label">mg/L</span>
|
|
609
|
+
</div>
|
|
610
|
+
</div>
|
|
611
|
+
<div class="form-group">
|
|
612
|
+
<label for="chlorophyll">${t.chlorophyll}<span class="unit">(${t.chlorophyllUnit})</span></label>
|
|
613
|
+
<div class="input-with-unit">
|
|
614
|
+
<input type="number" step="0.1" id="chlorophyll" name="chlorophyll">
|
|
615
|
+
<span class="unit-label">µg/L</span>
|
|
616
|
+
</div>
|
|
617
|
+
</div>
|
|
618
|
+
</div>
|
|
619
|
+
</div>
|
|
620
|
+
|
|
621
|
+
<!-- Biological Parameters -->
|
|
622
|
+
<div class="section">
|
|
623
|
+
<h2 class="section-title">${t.biologicalParams}</h2>
|
|
624
|
+
<div class="form-grid">
|
|
625
|
+
<div class="form-group">
|
|
626
|
+
<label for="coliform">${t.coliformBacteria}<span class="unit">(${t.coliformUnit})</span></label>
|
|
627
|
+
<div class="input-with-unit">
|
|
628
|
+
<input type="number" id="coliform" name="coliformBacteria">
|
|
629
|
+
<span class="unit-label">CFU</span>
|
|
630
|
+
</div>
|
|
631
|
+
</div>
|
|
632
|
+
<div class="form-group">
|
|
633
|
+
<label for="enterococci">${t.enterococci}<span class="unit">(${t.enterococciUnit})</span></label>
|
|
634
|
+
<div class="input-with-unit">
|
|
635
|
+
<input type="number" id="enterococci" name="enterococci">
|
|
636
|
+
<span class="unit-label">CFU</span>
|
|
637
|
+
</div>
|
|
638
|
+
</div>
|
|
639
|
+
<div class="form-group">
|
|
640
|
+
<label for="phytoplankton">${t.phytoplankton}<span class="unit">(${t.phytoplanktonUnit})</span></label>
|
|
641
|
+
<div class="input-with-unit">
|
|
642
|
+
<input type="number" id="phytoplankton" name="phytoplankton">
|
|
643
|
+
<span class="unit-label">cells/mL</span>
|
|
644
|
+
</div>
|
|
645
|
+
</div>
|
|
646
|
+
</div>
|
|
647
|
+
</div>
|
|
648
|
+
|
|
649
|
+
<!-- Visual Observations -->
|
|
650
|
+
<div class="section">
|
|
651
|
+
<h2 class="section-title">${t.visualObservations}</h2>
|
|
652
|
+
<div class="form-grid">
|
|
653
|
+
<div class="form-group">
|
|
654
|
+
<label for="water-color">${t.waterColor}</label>
|
|
655
|
+
<select id="water-color" name="waterColor">
|
|
656
|
+
<option value="clear">${t.colorOptions.clear}</option>
|
|
657
|
+
<option value="greenish">${t.colorOptions.greenish}</option>
|
|
658
|
+
<option value="brownish">${t.colorOptions.brownish}</option>
|
|
659
|
+
<option value="milky">${t.colorOptions.milky}</option>
|
|
660
|
+
<option value="other">${t.colorOptions.other}</option>
|
|
661
|
+
</select>
|
|
662
|
+
</div>
|
|
663
|
+
<div class="form-group">
|
|
664
|
+
<label for="odor">${t.odor}</label>
|
|
665
|
+
<select id="odor" name="odor">
|
|
666
|
+
<option value="none">${t.odorOptions.none}</option>
|
|
667
|
+
<option value="marine">${t.odorOptions.marine}</option>
|
|
668
|
+
<option value="sulfurous">${t.odorOptions.sulfurous}</option>
|
|
669
|
+
<option value="chemical">${t.odorOptions.chemical}</option>
|
|
670
|
+
<option value="other">${t.odorOptions.other}</option>
|
|
671
|
+
</select>
|
|
672
|
+
</div>
|
|
673
|
+
<div class="form-group">
|
|
674
|
+
<label for="floating-debris">${t.floatingDebris}</label>
|
|
675
|
+
<select id="floating-debris" name="floatingDebris">
|
|
676
|
+
<option value="none">${t.debrisOptions.none}</option>
|
|
677
|
+
<option value="minimal">${t.debrisOptions.minimal}</option>
|
|
678
|
+
<option value="moderate">${t.debrisOptions.moderate}</option>
|
|
679
|
+
<option value="significant">${t.debrisOptions.significant}</option>
|
|
680
|
+
</select>
|
|
681
|
+
</div>
|
|
682
|
+
<div class="form-group">
|
|
683
|
+
<label for="oil-presence">${t.oilPresence}</label>
|
|
684
|
+
<select id="oil-presence" name="oilPresence">
|
|
685
|
+
<option value="none">${t.oilOptions.none}</option>
|
|
686
|
+
<option value="sheen">${t.oilOptions.sheen}</option>
|
|
687
|
+
<option value="slick">${t.oilOptions.slick}</option>
|
|
688
|
+
<option value="heavy">${t.oilOptions.heavy}</option>
|
|
689
|
+
</select>
|
|
690
|
+
</div>
|
|
691
|
+
</div>
|
|
692
|
+
</div>
|
|
693
|
+
|
|
694
|
+
<!-- Weather Conditions -->
|
|
695
|
+
<div class="section">
|
|
696
|
+
<h2 class="section-title">${t.weather}</h2>
|
|
697
|
+
<div class="form-grid">
|
|
698
|
+
<div class="form-group">
|
|
699
|
+
<label for="weather-conditions">${t.weatherConditions}</label>
|
|
700
|
+
<select id="weather-conditions" name="weatherConditions">
|
|
701
|
+
<option value="sunny">${t.weatherOptions.sunny}</option>
|
|
702
|
+
<option value="cloudy">${t.weatherOptions.cloudy}</option>
|
|
703
|
+
<option value="rainy">${t.weatherOptions.rainy}</option>
|
|
704
|
+
<option value="stormy">${t.weatherOptions.stormy}</option>
|
|
705
|
+
</select>
|
|
706
|
+
</div>
|
|
707
|
+
<div class="form-group">
|
|
708
|
+
<label for="air-temperature">${t.airTemperature}<span class="unit">(${t.temperatureUnit})</span></label>
|
|
709
|
+
<div class="input-with-unit">
|
|
710
|
+
<input type="number" step="0.1" id="air-temperature" name="airTemperature">
|
|
711
|
+
<span class="unit-label">°C</span>
|
|
712
|
+
</div>
|
|
713
|
+
</div>
|
|
714
|
+
<div class="form-group">
|
|
715
|
+
<label for="wind-speed">${t.windSpeed}<span class="unit">(${t.windSpeedUnit})</span></label>
|
|
716
|
+
<div class="input-with-unit">
|
|
717
|
+
<input type="number" step="1" id="wind-speed" name="windSpeed">
|
|
718
|
+
<span class="unit-label">km/h</span>
|
|
719
|
+
</div>
|
|
720
|
+
</div>
|
|
721
|
+
<div class="form-group">
|
|
722
|
+
<label for="wave-height">${t.waveHeight}<span class="unit">(${t.waveHeightUnit})</span></label>
|
|
723
|
+
<div class="input-with-unit">
|
|
724
|
+
<input type="number" step="0.1" id="wave-height" name="waveHeight">
|
|
725
|
+
<span class="unit-label">m</span>
|
|
726
|
+
</div>
|
|
727
|
+
</div>
|
|
728
|
+
</div>
|
|
729
|
+
</div>
|
|
730
|
+
|
|
731
|
+
<!-- Notes -->
|
|
732
|
+
<div class="section">
|
|
733
|
+
<h2 class="section-title">${t.notes}</h2>
|
|
734
|
+
<div class="form-group full-width">
|
|
735
|
+
<textarea id="notes" name="notes" placeholder="${t.notesPlaceholder}"></textarea>
|
|
736
|
+
</div>
|
|
737
|
+
</div>
|
|
738
|
+
|
|
739
|
+
<!-- Quality Indicator -->
|
|
740
|
+
<div class="quality-indicator" id="quality-indicator" style="display:none">
|
|
741
|
+
<span>${t.qualityIndex}:</span>
|
|
742
|
+
<span class="quality-badge" id="quality-badge"></span>
|
|
743
|
+
</div>
|
|
744
|
+
|
|
745
|
+
<!-- Form Actions -->
|
|
746
|
+
<div class="form-actions">
|
|
747
|
+
<button type="button" class="btn btn-secondary" onclick="resetForm()">${t.reset}</button>
|
|
748
|
+
<button type="submit" class="btn btn-primary" id="submit-btn">${t.submit}</button>
|
|
749
|
+
</div>
|
|
750
|
+
</form>
|
|
751
|
+
</main>
|
|
752
|
+
|
|
753
|
+
<footer>
|
|
754
|
+
AutoCode - Water Quality Management
|
|
755
|
+
</footer>
|
|
756
|
+
|
|
757
|
+
<script>
|
|
758
|
+
const lang = '${lang}';
|
|
759
|
+
const translations = {
|
|
760
|
+
saving: '${t.saving}',
|
|
761
|
+
success: '${t.success}',
|
|
762
|
+
error: '${t.error}',
|
|
763
|
+
submit: '${t.submit}',
|
|
764
|
+
qualityGood: '${t.qualityGood}',
|
|
765
|
+
qualityModerate: '${t.qualityModerate}',
|
|
766
|
+
qualityPoor: '${t.qualityPoor}',
|
|
767
|
+
qualityCritical: '${t.qualityCritical}'
|
|
768
|
+
};
|
|
769
|
+
|
|
770
|
+
// Set default date and time
|
|
771
|
+
document.getElementById('sample-date').valueAsDate = new Date();
|
|
772
|
+
document.getElementById('sample-time').value = new Date().toTimeString().slice(0, 5);
|
|
773
|
+
|
|
774
|
+
// Switch language
|
|
775
|
+
function switchLang(newLang) {
|
|
776
|
+
window.location.href = '/water-quality?lang=' + newLang;
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
// Show notification
|
|
780
|
+
function showNotification(type, message) {
|
|
781
|
+
const notification = document.getElementById('notification');
|
|
782
|
+
notification.textContent = message;
|
|
783
|
+
notification.className = 'notification ' + type + ' show';
|
|
784
|
+
setTimeout(() => {
|
|
785
|
+
notification.classList.remove('show');
|
|
786
|
+
}, 3000);
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
// Reset form
|
|
790
|
+
function resetForm() {
|
|
791
|
+
document.getElementById('water-quality-form').reset();
|
|
792
|
+
document.getElementById('sample-date').valueAsDate = new Date();
|
|
793
|
+
document.getElementById('sample-time').value = new Date().toTimeString().slice(0, 5);
|
|
794
|
+
document.getElementById('quality-indicator').style.display = 'none';
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
// Calculate water quality index based on parameters
|
|
798
|
+
function calculateQualityIndex() {
|
|
799
|
+
const ph = parseFloat(document.getElementById('ph').value);
|
|
800
|
+
const dissolvedOxygen = parseFloat(document.getElementById('dissolved-oxygen').value);
|
|
801
|
+
const turbidity = parseFloat(document.getElementById('turbidity').value);
|
|
802
|
+
const coliform = parseFloat(document.getElementById('coliform').value);
|
|
803
|
+
|
|
804
|
+
let score = 100;
|
|
805
|
+
|
|
806
|
+
// pH scoring (optimal: 7.5-8.5 for seawater)
|
|
807
|
+
if (!isNaN(ph)) {
|
|
808
|
+
if (ph < 7.0 || ph > 9.0) score -= 30;
|
|
809
|
+
else if (ph < 7.5 || ph > 8.5) score -= 10;
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
// Dissolved oxygen scoring (optimal: > 6 mg/L)
|
|
813
|
+
if (!isNaN(dissolvedOxygen)) {
|
|
814
|
+
if (dissolvedOxygen < 2) score -= 40;
|
|
815
|
+
else if (dissolvedOxygen < 4) score -= 20;
|
|
816
|
+
else if (dissolvedOxygen < 6) score -= 10;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
// Turbidity scoring (optimal: < 5 NTU)
|
|
820
|
+
if (!isNaN(turbidity)) {
|
|
821
|
+
if (turbidity > 50) score -= 30;
|
|
822
|
+
else if (turbidity > 20) score -= 20;
|
|
823
|
+
else if (turbidity > 5) score -= 10;
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
// Coliform scoring (optimal: < 100 CFU/100mL)
|
|
827
|
+
if (!isNaN(coliform)) {
|
|
828
|
+
if (coliform > 1000) score -= 40;
|
|
829
|
+
else if (coliform > 500) score -= 25;
|
|
830
|
+
else if (coliform > 100) score -= 10;
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
return Math.max(0, score);
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
// Update quality indicator
|
|
837
|
+
function updateQualityIndicator() {
|
|
838
|
+
const score = calculateQualityIndex();
|
|
839
|
+
const indicator = document.getElementById('quality-indicator');
|
|
840
|
+
const badge = document.getElementById('quality-badge');
|
|
841
|
+
|
|
842
|
+
if (score >= 80) {
|
|
843
|
+
badge.textContent = translations.qualityGood + ' (' + score + ')';
|
|
844
|
+
badge.className = 'quality-badge quality-good';
|
|
845
|
+
} else if (score >= 60) {
|
|
846
|
+
badge.textContent = translations.qualityModerate + ' (' + score + ')';
|
|
847
|
+
badge.className = 'quality-badge quality-moderate';
|
|
848
|
+
} else if (score >= 40) {
|
|
849
|
+
badge.textContent = translations.qualityPoor + ' (' + score + ')';
|
|
850
|
+
badge.className = 'quality-badge quality-poor';
|
|
851
|
+
} else {
|
|
852
|
+
badge.textContent = translations.qualityCritical + ' (' + score + ')';
|
|
853
|
+
badge.className = 'quality-badge quality-critical';
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
indicator.style.display = 'flex';
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
// Add event listeners for quality calculation
|
|
860
|
+
['ph', 'dissolved-oxygen', 'turbidity', 'coliform'].forEach(id => {
|
|
861
|
+
document.getElementById(id).addEventListener('input', updateQualityIndicator);
|
|
862
|
+
});
|
|
863
|
+
|
|
864
|
+
// Handle form submission
|
|
865
|
+
document.getElementById('water-quality-form').addEventListener('submit', async function(e) {
|
|
866
|
+
e.preventDefault();
|
|
867
|
+
|
|
868
|
+
const submitBtn = document.getElementById('submit-btn');
|
|
869
|
+
submitBtn.disabled = true;
|
|
870
|
+
submitBtn.textContent = translations.saving;
|
|
871
|
+
|
|
872
|
+
const formData = new FormData(this);
|
|
873
|
+
const data = {};
|
|
874
|
+
formData.forEach((value, key) => {
|
|
875
|
+
if (value !== '') {
|
|
876
|
+
data[key] = value;
|
|
877
|
+
}
|
|
878
|
+
});
|
|
879
|
+
|
|
880
|
+
// Add timestamp and quality score
|
|
881
|
+
data.submittedAt = new Date().toISOString();
|
|
882
|
+
data.qualityScore = calculateQualityIndex();
|
|
883
|
+
|
|
884
|
+
try {
|
|
885
|
+
const response = await fetch('/api/water-quality', {
|
|
886
|
+
method: 'POST',
|
|
887
|
+
headers: {
|
|
888
|
+
'Content-Type': 'application/json'
|
|
889
|
+
},
|
|
890
|
+
body: JSON.stringify(data)
|
|
891
|
+
});
|
|
892
|
+
|
|
893
|
+
if (response.ok) {
|
|
894
|
+
showNotification('success', translations.success);
|
|
895
|
+
resetForm();
|
|
896
|
+
} else {
|
|
897
|
+
throw new Error('Failed to save');
|
|
898
|
+
}
|
|
899
|
+
} catch (error) {
|
|
900
|
+
showNotification('error', translations.error);
|
|
901
|
+
} finally {
|
|
902
|
+
submitBtn.disabled = false;
|
|
903
|
+
submitBtn.textContent = translations.submit;
|
|
904
|
+
}
|
|
905
|
+
});
|
|
906
|
+
</script>
|
|
907
|
+
</body>
|
|
908
|
+
</html>`;
|
|
909
|
+
}
|
|
910
|
+
//# sourceMappingURL=water-quality-form.js.map
|