@jsenv/navi 0.10.2 → 0.11.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/dist/jsenv_navi.js +13838 -23291
- package/dist/jsenv_navi.js.map +1281 -0
- package/package.json +6 -8
- package/index.js +0 -122
- package/src/action_private_properties.js +0 -11
- package/src/action_proxy_test.html +0 -353
- package/src/action_run_states.js +0 -5
- package/src/actions.js +0 -1401
- package/src/browser_integration/browser_integration.js +0 -216
- package/src/browser_integration/document_back_and_forward.js +0 -17
- package/src/browser_integration/document_loading_signal.js +0 -100
- package/src/browser_integration/document_state_signal.js +0 -9
- package/src/browser_integration/document_url_signal.js +0 -9
- package/src/browser_integration/use_is_visited.js +0 -19
- package/src/browser_integration/via_history.js +0 -232
- package/src/browser_integration/via_navigation.js +0 -168
- package/src/components/action_execution/form_context.js +0 -5
- package/src/components/action_execution/render_actionable_component.jsx +0 -29
- package/src/components/action_execution/use_action.js +0 -99
- package/src/components/action_execution/use_execute_action.js +0 -193
- package/src/components/action_execution/use_run_on_mount.js +0 -9
- package/src/components/action_renderer.jsx +0 -125
- package/src/components/callout/callout.js +0 -990
- package/src/components/callout/callout_demo.html +0 -201
- package/src/components/callout/test_dynamic_positioning.html +0 -161
- package/src/components/callout/test_html_document_iframe.html +0 -182
- package/src/components/demos/0_button_demo.html +0 -707
- package/src/components/demos/10_column_reordering_debug.html +0 -277
- package/src/components/demos/11_table_selection_debug.html +0 -432
- package/src/components/demos/1_checkbox_demo.html +0 -754
- package/src/components/demos/2_input_textual_demo.html +0 -286
- package/src/components/demos/3_radio_demo.html +0 -874
- package/src/components/demos/4_select_demo.html +0 -100
- package/src/components/demos/5_list_scrollable_demo.html +0 -153
- package/src/components/demos/6_tablist_demo.html +0 -77
- package/src/components/demos/7_table_selection_demo.html +0 -176
- package/src/components/demos/8_table_fixed_headers_demo.html +0 -584
- package/src/components/demos/9_table_column_drag_demo.html +0 -325
- package/src/components/demos/action/0_button_demo.html +0 -204
- package/src/components/demos/action/10_shortcuts_demo.html +0 -189
- package/src/components/demos/action/11_nested_shortcuts_demo.xhtml +0 -401
- package/src/components/demos/action/1_input_text_demo.html +0 -876
- package/src/components/demos/action/2_form_multiple.html +0 -303
- package/src/components/demos/action/3_details_demo.html +0 -203
- package/src/components/demos/action/4_input_checkbox_demo.html +0 -731
- package/src/components/demos/action/5_input_checkbox_state_demo.html +0 -270
- package/src/components/demos/action/6_checkbox_list_demo.html +0 -341
- package/src/components/demos/action/7_radio_list_demo.html +0 -357
- package/src/components/demos/action/8_editable_demo.html +0 -431
- package/src/components/demos/action/9_link_demo.html +0 -194
- package/src/components/demos/demo.md +0 -0
- package/src/components/demos/route/basic/basic.html +0 -14
- package/src/components/demos/route/basic/basic_route_demo.jsx +0 -224
- package/src/components/demos/route/multi/multi.html +0 -14
- package/src/components/demos/route/multi/multi_route_demo.jsx +0 -277
- package/src/components/demos/ui_transition/0_action_renderer_ui_transition_demo.html +0 -695
- package/src/components/demos/ui_transition/1_nested_ui_transition_demo.html +0 -429
- package/src/components/demos/ui_transition/2_height_transition_test.html +0 -295
- package/src/components/details/details.jsx +0 -245
- package/src/components/details/summary_marker.jsx +0 -141
- package/src/components/edition/editable.jsx +0 -186
- package/src/components/error_boundary_context.js +0 -9
- package/src/components/field/README.md +0 -247
- package/src/components/field/button.jsx +0 -429
- package/src/components/field/checkbox_list.jsx +0 -185
- package/src/components/field/collect_form_element_values.js +0 -82
- package/src/components/field/custom_field.js +0 -106
- package/src/components/field/form.jsx +0 -209
- package/src/components/field/input.jsx +0 -16
- package/src/components/field/input_checkbox.jsx +0 -434
- package/src/components/field/input_radio.jsx +0 -432
- package/src/components/field/input_textual.jsx +0 -389
- package/src/components/field/label.jsx +0 -46
- package/src/components/field/radio_list.jsx +0 -183
- package/src/components/field/select.jsx +0 -256
- package/src/components/field/use_action_events.js +0 -132
- package/src/components/field/use_form_events.js +0 -59
- package/src/components/field/use_ui_state_controller.js +0 -506
- package/src/components/item_tracker/README.md +0 -461
- package/src/components/item_tracker/use_isolated_item_tracker.jsx +0 -209
- package/src/components/item_tracker/use_isolated_item_tracker_demo.html +0 -148
- package/src/components/item_tracker/use_isolated_item_tracker_demo.jsx +0 -460
- package/src/components/item_tracker/use_item_tracker.jsx +0 -143
- package/src/components/item_tracker/use_item_tracker_demo.html +0 -207
- package/src/components/item_tracker/use_item_tracker_demo.jsx +0 -216
- package/src/components/keyboard_shortcuts/active_keyboard_shortcuts.jsx +0 -87
- package/src/components/keyboard_shortcuts/aria_key_shortcuts.js +0 -61
- package/src/components/keyboard_shortcuts/keyboard_key_meta.js +0 -17
- package/src/components/keyboard_shortcuts/keyboard_shortcuts.js +0 -371
- package/src/components/keyboard_shortcuts/os.js +0 -9
- package/src/components/layout/demos/demo_flex.html +0 -638
- package/src/components/layout/demos/demo_layout_style_buttons.html +0 -351
- package/src/components/layout/demos/demo_layout_style_input.html +0 -226
- package/src/components/layout/demos/demo_layout_style_text.html +0 -514
- package/src/components/layout/flex.jsx +0 -109
- package/src/components/layout/layout_context.jsx +0 -3
- package/src/components/layout/spacing.jsx +0 -20
- package/src/components/layout/use_layout_style.js +0 -249
- package/src/components/link/link.jsx +0 -267
- package/src/components/link/link_with_icon.jsx +0 -52
- package/src/components/loader/loader_background.jsx +0 -372
- package/src/components/loader/loading_spinner.jsx +0 -68
- package/src/components/loader/network_speed.js +0 -83
- package/src/components/loader/rectangle_loading.jsx +0 -244
- package/src/components/props_composition/demos/demo_with_props_style.html +0 -81
- package/src/components/props_composition/with_props_class_name.js +0 -37
- package/src/components/props_composition/with_props_style.js +0 -26
- package/src/components/route.jsx +0 -19
- package/src/components/selection/selection.jsx +0 -1583
- package/src/components/svg/font_sized_svg.jsx +0 -59
- package/src/components/svg/icon_and_text.jsx +0 -21
- package/src/components/svg/svg_mask_overlay.jsx +0 -105
- package/src/components/table/drag/table_drag.jsx +0 -506
- package/src/components/table/resize/table_resize.jsx +0 -650
- package/src/components/table/resize/table_size.js +0 -43
- package/src/components/table/selection/table_selection.js +0 -106
- package/src/components/table/selection/table_selection.jsx +0 -203
- package/src/components/table/sticky/sticky_group.js +0 -354
- package/src/components/table/sticky/table_sticky.js +0 -25
- package/src/components/table/sticky/table_sticky.jsx +0 -501
- package/src/components/table/table.jsx +0 -721
- package/src/components/table/table_css.js +0 -211
- package/src/components/table/table_ui.jsx +0 -49
- package/src/components/table/use_cells_and_columns.js +0 -90
- package/src/components/table/use_object_array_to_cells.js +0 -46
- package/src/components/table/z_indexes.js +0 -23
- package/src/components/tablist/tablist.jsx +0 -99
- package/src/components/text/demos/demo_text_and_icon.html +0 -421
- package/src/components/text/overflow.jsx +0 -15
- package/src/components/text/text.jsx +0 -83
- package/src/components/text/text_and_count.jsx +0 -28
- package/src/components/ui_transition.jsx +0 -128
- package/src/components/use_auto_focus.js +0 -94
- package/src/components/use_batch_during_render.js +0 -33
- package/src/components/use_debounce_true.js +0 -31
- package/src/components/use_dependencies_diff.js +0 -35
- package/src/components/use_focus_group.js +0 -20
- package/src/components/use_initial_value.js +0 -78
- package/src/components/use_is_visited.js +0 -19
- package/src/components/use_ref_array.js +0 -38
- package/src/components/use_signal_sync.js +0 -50
- package/src/components/use_stable_callback.js +0 -68
- package/src/components/use_state_array.js +0 -47
- package/src/docs/actions.md +0 -250
- package/src/docs/demos/resource/action_status.jsx +0 -42
- package/src/docs/demos/resource/demo.md +0 -1
- package/src/docs/demos/resource/resource_demo_0.html +0 -84
- package/src/docs/demos/resource/resource_demo_10_post_gc.html +0 -364
- package/src/docs/demos/resource/resource_demo_11_describe_many.html +0 -362
- package/src/docs/demos/resource/resource_demo_2.html +0 -173
- package/src/docs/demos/resource/resource_demo_3_filtered_users.html +0 -415
- package/src/docs/demos/resource/resource_demo_4_details.html +0 -284
- package/src/docs/demos/resource/resource_demo_5_renderer_lazy.html +0 -115
- package/src/docs/demos/resource/resource_demo_6_gc.html +0 -217
- package/src/docs/demos/resource/resource_demo_7_child_gc.html +0 -240
- package/src/docs/demos/resource/resource_demo_8_proxy_gc.html +0 -319
- package/src/docs/demos/resource/resource_demo_9_describe_one.html +0 -472
- package/src/docs/demos/resource/tata.jsx +0 -3
- package/src/docs/demos/resource/toto.jsx +0 -3
- package/src/docs/demos/user_nav/user_nav.html +0 -12
- package/src/docs/demos/user_nav/user_nav.jsx +0 -330
- package/src/docs/resource_dependencies.md +0 -103
- package/src/docs/resource_with_params.md +0 -80
- package/src/navi_css_vars.js +0 -14
- package/src/notes.md +0 -34
- package/src/route/route.js +0 -596
- package/src/route/route.xtest.html +0 -228
- package/src/store/array_signal_store.js +0 -537
- package/src/store/local_storage_signal.js +0 -17
- package/src/store/resource_graph.js +0 -1304
- package/src/store/tests/resource_graph_autoreload_demo.html +0 -12
- package/src/store/tests/resource_graph_autoreload_demo.jsx +0 -964
- package/src/store/tests/resource_graph_dependencies.test_manual.js +0 -95
- package/src/store/value_in_local_storage.js +0 -187
- package/src/symbol_object_signal.js +0 -1
- package/src/use_action_data.js +0 -10
- package/src/use_action_status.js +0 -47
- package/src/utils/add_many_event_listeners.js +0 -15
- package/src/utils/array_add_remove.js +0 -61
- package/src/utils/array_signal.js +0 -15
- package/src/utils/compare_two_js_values.js +0 -172
- package/src/utils/execute_with_cleanup.js +0 -21
- package/src/utils/get_caller_info.js +0 -85
- package/src/utils/is_signal.js +0 -20
- package/src/utils/js_value_weak_map.js +0 -162
- package/src/utils/js_value_weak_map_demo.html +0 -690
- package/src/utils/merge_two_js_values.js +0 -53
- package/src/utils/stringify_for_display.js +0 -131
- package/src/utils/weak_effect.js +0 -48
- package/src/validation/constraints/confirm_constraint.js +0 -14
- package/src/validation/constraints/create_unique_value_constraint.js +0 -27
- package/src/validation/constraints/native_constraints.js +0 -338
- package/src/validation/constraints/readonly_constraint.js +0 -41
- package/src/validation/constraints/same_as_constraint.js +0 -42
- package/src/validation/constraints/single_space_constraint.js +0 -13
- package/src/validation/custom_constraint_validation.js +0 -793
- package/src/validation/custom_message.js +0 -18
- package/src/validation/demos/browser_style.png +0 -0
- package/src/validation/demos/demo_same_as_constraint.html +0 -259
- package/src/validation/demos/form_validation_demo.html +0 -142
- package/src/validation/demos/form_validation_demo_preact.html +0 -87
- package/src/validation/demos/form_validation_native_popover_demo.html +0 -168
- package/src/validation/demos/form_validation_vs_native_demo.html +0 -172
- package/src/validation/hooks/use_constraints.js +0 -23
- package/src/validation/hooks/use_custom_validation_ref.js +0 -73
- package/src/validation/hooks/use_validation_message.js +0 -19
- package/src/validation/input_change_effect.js +0 -106
|
@@ -1,964 +0,0 @@
|
|
|
1
|
-
import { ActionRenderer, resource, useActionStatus } from "@jsenv/navi";
|
|
2
|
-
import { render } from "preact";
|
|
3
|
-
import { useEffect, useState } from "preact/hooks";
|
|
4
|
-
|
|
5
|
-
const hardcodedUsers = [
|
|
6
|
-
{ id: "1", name: "Alice", age: 30 },
|
|
7
|
-
{ id: "2", name: "Bob", age: 25 },
|
|
8
|
-
{ id: "3", name: "Charlie", age: 22 },
|
|
9
|
-
];
|
|
10
|
-
|
|
11
|
-
// Simulation d'un store en mémoire pour les tests
|
|
12
|
-
const userStore = new Map(hardcodedUsers.map((user) => [user.name, user]));
|
|
13
|
-
let nextId = 4;
|
|
14
|
-
|
|
15
|
-
// Compteurs de chargement par utilisateur
|
|
16
|
-
const loadCounters = new Map();
|
|
17
|
-
|
|
18
|
-
const USER = resource("user", {
|
|
19
|
-
idKey: "id",
|
|
20
|
-
mutableIdKeys: ["name"],
|
|
21
|
-
|
|
22
|
-
GET_MANY: () => {
|
|
23
|
-
console.log("🔍 GET_MANY users");
|
|
24
|
-
return Array.from(userStore.values());
|
|
25
|
-
},
|
|
26
|
-
|
|
27
|
-
GET: async ({ name }) => {
|
|
28
|
-
console.log(`🔍 GET user: ${name}`);
|
|
29
|
-
// Incrémenter le compteur pour cet utilisateur
|
|
30
|
-
const currentCount = loadCounters.get(name) || 0;
|
|
31
|
-
loadCounters.set(name, currentCount + 1);
|
|
32
|
-
console.log(`📊 Load count for ${name}: ${currentCount + 1}`);
|
|
33
|
-
|
|
34
|
-
await new Promise((resolve) => setTimeout(resolve, 800)); // Simuler un délai de chargement
|
|
35
|
-
|
|
36
|
-
const user = userStore.get(name);
|
|
37
|
-
if (!user) {
|
|
38
|
-
throw new Error(`User ${name} not found`);
|
|
39
|
-
}
|
|
40
|
-
return user;
|
|
41
|
-
},
|
|
42
|
-
|
|
43
|
-
POST: ({ name, age }) => {
|
|
44
|
-
console.log(`➕ POST user: ${name}, age: ${age}`);
|
|
45
|
-
if (userStore.has(name)) {
|
|
46
|
-
throw new Error(`User ${name} already exists`);
|
|
47
|
-
}
|
|
48
|
-
const newUser = { id: String(nextId++), name, age };
|
|
49
|
-
userStore.set(name, newUser);
|
|
50
|
-
return newUser;
|
|
51
|
-
},
|
|
52
|
-
|
|
53
|
-
DELETE: ({ name }) => {
|
|
54
|
-
console.log(`🗑️ DELETE user: ${name}`);
|
|
55
|
-
const user = userStore.get(name);
|
|
56
|
-
if (!user) {
|
|
57
|
-
throw new Error(`User ${name} not found`);
|
|
58
|
-
}
|
|
59
|
-
userStore.delete(name);
|
|
60
|
-
return user;
|
|
61
|
-
},
|
|
62
|
-
|
|
63
|
-
DELETE_MANY: ({ names }) => {
|
|
64
|
-
for (const name of names) {
|
|
65
|
-
const user = userStore.get(name);
|
|
66
|
-
if (!user) {
|
|
67
|
-
throw new Error(`User ${name} not found`);
|
|
68
|
-
}
|
|
69
|
-
userStore.delete(name);
|
|
70
|
-
}
|
|
71
|
-
return names.map((name) => ({ name }));
|
|
72
|
-
},
|
|
73
|
-
|
|
74
|
-
PUT: ({ name, newName, age }) => {
|
|
75
|
-
console.log(`✏️ PUT user: ${name} -> ${newName || name}, age: ${age}`);
|
|
76
|
-
const user = userStore.get(name);
|
|
77
|
-
if (!user) {
|
|
78
|
-
throw new Error(`User ${name} not found`);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Si le nom change, supprimer l'ancienne entrée
|
|
82
|
-
if (newName && newName !== name) {
|
|
83
|
-
userStore.delete(name);
|
|
84
|
-
user.name = newName;
|
|
85
|
-
userStore.set(newName, user);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
if (age !== undefined) {
|
|
89
|
-
user.age = age;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
return user;
|
|
93
|
-
},
|
|
94
|
-
|
|
95
|
-
PATCH: ({ name, newName, age }) => {
|
|
96
|
-
console.log(
|
|
97
|
-
`📝 PATCH user: ${name}${newName ? ` -> ${newName}` : ""}, age: ${age}`,
|
|
98
|
-
);
|
|
99
|
-
const user = userStore.get(name);
|
|
100
|
-
if (!user) {
|
|
101
|
-
throw new Error(`User ${name} not found`);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Si le nom change, supprimer l'ancienne entrée
|
|
105
|
-
if (newName && newName !== name) {
|
|
106
|
-
userStore.delete(name);
|
|
107
|
-
user.name = newName;
|
|
108
|
-
userStore.set(newName, user);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (age !== undefined) {
|
|
112
|
-
user.age = age;
|
|
113
|
-
}
|
|
114
|
-
return user;
|
|
115
|
-
},
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
// Fonction utilitaire pour obtenir le compteur d'un utilisateur
|
|
119
|
-
const getLoadCount = (name) => loadCounters.get(name) || 0;
|
|
120
|
-
|
|
121
|
-
// Actions préparées pour les tests
|
|
122
|
-
const allUsersAction = USER.GET_MANY;
|
|
123
|
-
const aliceAction = USER.GET.bindParams({ name: "Alice" });
|
|
124
|
-
const bobAction = USER.GET.bindParams({ name: "Bob" });
|
|
125
|
-
const charlieAction = USER.GET.bindParams({ name: "Charlie" });
|
|
126
|
-
const testUserAction = USER.GET.bindParams({ name: "TestUser" }); // Utilisateur qui n'existe pas encore
|
|
127
|
-
|
|
128
|
-
const UserCard = ({ name, action }) => {
|
|
129
|
-
const [isEditing, setIsEditing] = useState(false);
|
|
130
|
-
const [editName, setEditName] = useState("");
|
|
131
|
-
const [editAge, setEditAge] = useState("");
|
|
132
|
-
const [loadCount, setLoadCount] = useState(0);
|
|
133
|
-
|
|
134
|
-
// Get common data from action status
|
|
135
|
-
const { data } = useActionStatus(action);
|
|
136
|
-
const displayName = data ? `${data.name} (${data.age} ans)` : name;
|
|
137
|
-
|
|
138
|
-
// Charger automatiquement au démarrage pour Alice, Bob et Charlie
|
|
139
|
-
useEffect(() => {
|
|
140
|
-
if (
|
|
141
|
-
name === "Alice" ||
|
|
142
|
-
name === "Bob" ||
|
|
143
|
-
name === "Charlie" ||
|
|
144
|
-
name === "TestUser"
|
|
145
|
-
) {
|
|
146
|
-
console.log(`🚀 Auto-run ${name} on mount`);
|
|
147
|
-
action.run();
|
|
148
|
-
}
|
|
149
|
-
}, [name, action]);
|
|
150
|
-
|
|
151
|
-
// Mettre à jour le compteur périodiquement pour refléter les changements
|
|
152
|
-
useEffect(() => {
|
|
153
|
-
const interval = setInterval(() => {
|
|
154
|
-
setLoadCount(getLoadCount(name));
|
|
155
|
-
}, 100);
|
|
156
|
-
return () => clearInterval(interval);
|
|
157
|
-
}, [name]);
|
|
158
|
-
|
|
159
|
-
const deleteUser = async (data) => {
|
|
160
|
-
if (!data) return;
|
|
161
|
-
try {
|
|
162
|
-
await USER.DELETE({ name: data.name });
|
|
163
|
-
} catch (err) {
|
|
164
|
-
console.error("Erreur lors de la suppression:", err);
|
|
165
|
-
}
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
const renameUser = async (data) => {
|
|
169
|
-
if (!data || !editName.trim()) return;
|
|
170
|
-
try {
|
|
171
|
-
await USER.PUT({ name: data.name, newName: editName.trim() });
|
|
172
|
-
setIsEditing(false);
|
|
173
|
-
} catch (err) {
|
|
174
|
-
console.error("Erreur lors du renommage:", err);
|
|
175
|
-
}
|
|
176
|
-
};
|
|
177
|
-
|
|
178
|
-
const updateAge = async (data) => {
|
|
179
|
-
if (!data || !editAge.trim()) return;
|
|
180
|
-
try {
|
|
181
|
-
await USER.PATCH({ name: data.name, age: parseInt(editAge) });
|
|
182
|
-
setIsEditing(false);
|
|
183
|
-
} catch (err) {
|
|
184
|
-
console.error("Erreur lors de la mise à jour de l'âge:", err);
|
|
185
|
-
}
|
|
186
|
-
};
|
|
187
|
-
|
|
188
|
-
const updateNameAndAge = async (data) => {
|
|
189
|
-
if (!data || !editName.trim() || !editAge.trim()) return;
|
|
190
|
-
try {
|
|
191
|
-
await USER.PUT({
|
|
192
|
-
name: data.name,
|
|
193
|
-
newName: editName.trim(),
|
|
194
|
-
age: parseInt(editAge),
|
|
195
|
-
});
|
|
196
|
-
setIsEditing(false);
|
|
197
|
-
} catch (err) {
|
|
198
|
-
console.error("Erreur lors de la mise à jour complète:", err);
|
|
199
|
-
}
|
|
200
|
-
};
|
|
201
|
-
|
|
202
|
-
// Common UI wrapper component
|
|
203
|
-
const CardWrapper = ({ children, statusColor, statusText }) => (
|
|
204
|
-
<div
|
|
205
|
-
style={{
|
|
206
|
-
border: "1px solid #dee2e6",
|
|
207
|
-
borderRadius: "8px",
|
|
208
|
-
padding: "16px",
|
|
209
|
-
margin: "8px 0",
|
|
210
|
-
backgroundColor: "#f8f9fa",
|
|
211
|
-
}}
|
|
212
|
-
>
|
|
213
|
-
<div
|
|
214
|
-
style={{
|
|
215
|
-
display: "flex",
|
|
216
|
-
justifyContent: "space-between",
|
|
217
|
-
alignItems: "center",
|
|
218
|
-
marginBottom: "12px",
|
|
219
|
-
}}
|
|
220
|
-
>
|
|
221
|
-
<h4 style={{ margin: 0, color: "#007bff" }}>{displayName}</h4>
|
|
222
|
-
<div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
|
|
223
|
-
<span
|
|
224
|
-
style={{
|
|
225
|
-
padding: "2px 6px",
|
|
226
|
-
borderRadius: "4px",
|
|
227
|
-
fontSize: "0.7em",
|
|
228
|
-
fontWeight: "bold",
|
|
229
|
-
backgroundColor: "#6c757d",
|
|
230
|
-
color: "white",
|
|
231
|
-
}}
|
|
232
|
-
>
|
|
233
|
-
📊 {loadCount}x
|
|
234
|
-
</span>
|
|
235
|
-
<span
|
|
236
|
-
style={{
|
|
237
|
-
padding: "4px 8px",
|
|
238
|
-
borderRadius: "4px",
|
|
239
|
-
fontSize: "0.8em",
|
|
240
|
-
fontWeight: "bold",
|
|
241
|
-
backgroundColor: statusColor,
|
|
242
|
-
color: "white",
|
|
243
|
-
}}
|
|
244
|
-
>
|
|
245
|
-
{statusText}
|
|
246
|
-
</span>
|
|
247
|
-
</div>
|
|
248
|
-
</div>
|
|
249
|
-
{children}
|
|
250
|
-
</div>
|
|
251
|
-
);
|
|
252
|
-
|
|
253
|
-
return (
|
|
254
|
-
<ActionRenderer action={action}>
|
|
255
|
-
{{
|
|
256
|
-
loading: () => (
|
|
257
|
-
<CardWrapper statusColor="#ffc107" statusText="Chargement...">
|
|
258
|
-
<div style={{ display: "flex", gap: "8px", flexWrap: "wrap" }}>
|
|
259
|
-
<button
|
|
260
|
-
onClick={() => action.rerun()}
|
|
261
|
-
disabled={true}
|
|
262
|
-
style={{
|
|
263
|
-
padding: "6px 12px",
|
|
264
|
-
backgroundColor: "#17a2b8",
|
|
265
|
-
color: "white",
|
|
266
|
-
border: "none",
|
|
267
|
-
borderRadius: "4px",
|
|
268
|
-
cursor: "not-allowed",
|
|
269
|
-
opacity: 0.6,
|
|
270
|
-
}}
|
|
271
|
-
>
|
|
272
|
-
🔄 Recharger
|
|
273
|
-
</button>
|
|
274
|
-
</div>
|
|
275
|
-
</CardWrapper>
|
|
276
|
-
),
|
|
277
|
-
|
|
278
|
-
error: (error) => (
|
|
279
|
-
<CardWrapper
|
|
280
|
-
statusColor="#dc3545"
|
|
281
|
-
statusText={
|
|
282
|
-
error.message && error.message.includes("not found")
|
|
283
|
-
? "404 - Non trouvé"
|
|
284
|
-
: "Erreur"
|
|
285
|
-
}
|
|
286
|
-
>
|
|
287
|
-
<div
|
|
288
|
-
style={{
|
|
289
|
-
color: "#dc3545",
|
|
290
|
-
backgroundColor: "#f8d7da",
|
|
291
|
-
padding: "8px",
|
|
292
|
-
borderRadius: "4px",
|
|
293
|
-
marginBottom: "8px",
|
|
294
|
-
}}
|
|
295
|
-
>
|
|
296
|
-
{error.message || error}
|
|
297
|
-
</div>
|
|
298
|
-
<div style={{ display: "flex", gap: "8px", flexWrap: "wrap" }}>
|
|
299
|
-
<button
|
|
300
|
-
onClick={() => action.rerun()}
|
|
301
|
-
style={{
|
|
302
|
-
padding: "6px 12px",
|
|
303
|
-
backgroundColor: "#17a2b8",
|
|
304
|
-
color: "white",
|
|
305
|
-
border: "none",
|
|
306
|
-
borderRadius: "4px",
|
|
307
|
-
}}
|
|
308
|
-
>
|
|
309
|
-
🔄 Recharger
|
|
310
|
-
</button>
|
|
311
|
-
</div>
|
|
312
|
-
</CardWrapper>
|
|
313
|
-
),
|
|
314
|
-
|
|
315
|
-
completed: (data) => {
|
|
316
|
-
// Initialiser les champs d'édition quand les données arrivent
|
|
317
|
-
if (data && !isEditing && (!editName || !editAge)) {
|
|
318
|
-
setEditName(data.name);
|
|
319
|
-
setEditAge(data.age.toString());
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
return (
|
|
323
|
-
<CardWrapper statusColor="#28a745" statusText="Chargé">
|
|
324
|
-
{!isEditing && (
|
|
325
|
-
<div
|
|
326
|
-
style={{
|
|
327
|
-
fontFamily: "monospace",
|
|
328
|
-
backgroundColor: "white",
|
|
329
|
-
padding: "8px",
|
|
330
|
-
borderRadius: "4px",
|
|
331
|
-
marginBottom: "8px",
|
|
332
|
-
}}
|
|
333
|
-
>
|
|
334
|
-
ID: {data.id}
|
|
335
|
-
<br />
|
|
336
|
-
Nom: {data.name}
|
|
337
|
-
<br />
|
|
338
|
-
Âge: {data.age} ans
|
|
339
|
-
</div>
|
|
340
|
-
)}
|
|
341
|
-
|
|
342
|
-
{isEditing && (
|
|
343
|
-
<div
|
|
344
|
-
style={{
|
|
345
|
-
backgroundColor: "white",
|
|
346
|
-
padding: "12px",
|
|
347
|
-
borderRadius: "4px",
|
|
348
|
-
marginBottom: "8px",
|
|
349
|
-
}}
|
|
350
|
-
>
|
|
351
|
-
<div style={{ marginBottom: "8px" }}>
|
|
352
|
-
<label
|
|
353
|
-
style={{
|
|
354
|
-
display: "block",
|
|
355
|
-
marginBottom: "4px",
|
|
356
|
-
fontWeight: "bold",
|
|
357
|
-
}}
|
|
358
|
-
>
|
|
359
|
-
Nom:
|
|
360
|
-
</label>
|
|
361
|
-
<input
|
|
362
|
-
type="text"
|
|
363
|
-
value={editName}
|
|
364
|
-
onChange={(e) => setEditName(e.target.value)}
|
|
365
|
-
style={{
|
|
366
|
-
width: "100%",
|
|
367
|
-
padding: "6px",
|
|
368
|
-
border: "1px solid #ced4da",
|
|
369
|
-
borderRadius: "4px",
|
|
370
|
-
}}
|
|
371
|
-
/>
|
|
372
|
-
</div>
|
|
373
|
-
<div style={{ marginBottom: "8px" }}>
|
|
374
|
-
<label
|
|
375
|
-
style={{
|
|
376
|
-
display: "block",
|
|
377
|
-
marginBottom: "4px",
|
|
378
|
-
fontWeight: "bold",
|
|
379
|
-
}}
|
|
380
|
-
>
|
|
381
|
-
Âge:
|
|
382
|
-
</label>
|
|
383
|
-
<input
|
|
384
|
-
type="number"
|
|
385
|
-
value={editAge}
|
|
386
|
-
onChange={(e) => setEditAge(e.target.value)}
|
|
387
|
-
style={{
|
|
388
|
-
width: "100%",
|
|
389
|
-
padding: "6px",
|
|
390
|
-
border: "1px solid #ced4da",
|
|
391
|
-
borderRadius: "4px",
|
|
392
|
-
}}
|
|
393
|
-
/>
|
|
394
|
-
</div>
|
|
395
|
-
<div
|
|
396
|
-
style={{ display: "flex", gap: "8px", flexWrap: "wrap" }}
|
|
397
|
-
>
|
|
398
|
-
<button
|
|
399
|
-
onClick={() => renameUser(data)}
|
|
400
|
-
style={{
|
|
401
|
-
padding: "4px 8px",
|
|
402
|
-
backgroundColor: "#ffc107",
|
|
403
|
-
color: "white",
|
|
404
|
-
border: "none",
|
|
405
|
-
borderRadius: "4px",
|
|
406
|
-
fontSize: "0.8em",
|
|
407
|
-
}}
|
|
408
|
-
>
|
|
409
|
-
Renommer
|
|
410
|
-
</button>
|
|
411
|
-
<button
|
|
412
|
-
onClick={() => updateAge(data)}
|
|
413
|
-
style={{
|
|
414
|
-
padding: "4px 8px",
|
|
415
|
-
backgroundColor: "#17a2b8",
|
|
416
|
-
color: "white",
|
|
417
|
-
border: "none",
|
|
418
|
-
borderRadius: "4px",
|
|
419
|
-
fontSize: "0.8em",
|
|
420
|
-
}}
|
|
421
|
-
>
|
|
422
|
-
Changer âge
|
|
423
|
-
</button>
|
|
424
|
-
<button
|
|
425
|
-
onClick={() => updateNameAndAge(data)}
|
|
426
|
-
style={{
|
|
427
|
-
padding: "4px 8px",
|
|
428
|
-
backgroundColor: "#6f42c1",
|
|
429
|
-
color: "white",
|
|
430
|
-
border: "none",
|
|
431
|
-
borderRadius: "4px",
|
|
432
|
-
fontSize: "0.8em",
|
|
433
|
-
}}
|
|
434
|
-
>
|
|
435
|
-
Tout modifier
|
|
436
|
-
</button>
|
|
437
|
-
<button
|
|
438
|
-
onClick={() => setIsEditing(false)}
|
|
439
|
-
style={{
|
|
440
|
-
padding: "4px 8px",
|
|
441
|
-
backgroundColor: "#6c757d",
|
|
442
|
-
color: "white",
|
|
443
|
-
border: "none",
|
|
444
|
-
borderRadius: "4px",
|
|
445
|
-
fontSize: "0.8em",
|
|
446
|
-
}}
|
|
447
|
-
>
|
|
448
|
-
Annuler
|
|
449
|
-
</button>
|
|
450
|
-
</div>
|
|
451
|
-
</div>
|
|
452
|
-
)}
|
|
453
|
-
|
|
454
|
-
<div style={{ display: "flex", gap: "8px", flexWrap: "wrap" }}>
|
|
455
|
-
<button
|
|
456
|
-
onClick={() => action.rerun()}
|
|
457
|
-
style={{
|
|
458
|
-
padding: "6px 12px",
|
|
459
|
-
backgroundColor: "#17a2b8",
|
|
460
|
-
color: "white",
|
|
461
|
-
border: "none",
|
|
462
|
-
borderRadius: "4px",
|
|
463
|
-
}}
|
|
464
|
-
>
|
|
465
|
-
🔄 Recharger
|
|
466
|
-
</button>
|
|
467
|
-
<button
|
|
468
|
-
onClick={() => setIsEditing(!isEditing)}
|
|
469
|
-
style={{
|
|
470
|
-
padding: "6px 12px",
|
|
471
|
-
backgroundColor: "#007bff",
|
|
472
|
-
color: "white",
|
|
473
|
-
border: "none",
|
|
474
|
-
borderRadius: "4px",
|
|
475
|
-
}}
|
|
476
|
-
>
|
|
477
|
-
✏️ Modifier
|
|
478
|
-
</button>
|
|
479
|
-
<button
|
|
480
|
-
onClick={() => deleteUser(data)}
|
|
481
|
-
style={{
|
|
482
|
-
padding: "6px 12px",
|
|
483
|
-
backgroundColor: "#dc3545",
|
|
484
|
-
color: "white",
|
|
485
|
-
border: "none",
|
|
486
|
-
borderRadius: "4px",
|
|
487
|
-
}}
|
|
488
|
-
>
|
|
489
|
-
🗑️ Supprimer
|
|
490
|
-
</button>
|
|
491
|
-
</div>
|
|
492
|
-
</CardWrapper>
|
|
493
|
-
);
|
|
494
|
-
},
|
|
495
|
-
|
|
496
|
-
idle: () => (
|
|
497
|
-
<CardWrapper statusColor="#6c757d" statusText="Non chargé">
|
|
498
|
-
<div style={{ display: "flex", gap: "8px", flexWrap: "wrap" }}>
|
|
499
|
-
<button
|
|
500
|
-
onClick={() => action.rerun()}
|
|
501
|
-
style={{
|
|
502
|
-
padding: "6px 12px",
|
|
503
|
-
backgroundColor: "#17a2b8",
|
|
504
|
-
color: "white",
|
|
505
|
-
border: "none",
|
|
506
|
-
borderRadius: "4px",
|
|
507
|
-
}}
|
|
508
|
-
>
|
|
509
|
-
� Charger
|
|
510
|
-
</button>
|
|
511
|
-
</div>
|
|
512
|
-
</CardWrapper>
|
|
513
|
-
),
|
|
514
|
-
}}
|
|
515
|
-
</ActionRenderer>
|
|
516
|
-
);
|
|
517
|
-
};
|
|
518
|
-
|
|
519
|
-
const UsersList = () => {
|
|
520
|
-
const [users, setUsers] = useState([]); // Initialiser avec un tableau vide au lieu de []
|
|
521
|
-
const [status, setStatus] = useState("idle");
|
|
522
|
-
|
|
523
|
-
const loadAllUsers = async () => {
|
|
524
|
-
try {
|
|
525
|
-
setStatus("loading");
|
|
526
|
-
// Utiliser la nouvelle syntaxe fonction directement
|
|
527
|
-
const result = await allUsersAction();
|
|
528
|
-
setUsers(result || []); // S'assurer qu'on a toujours un tableau
|
|
529
|
-
setStatus("success");
|
|
530
|
-
} catch (err) {
|
|
531
|
-
console.error("Erreur lors du chargement des utilisateurs:", err);
|
|
532
|
-
setUsers([]); // Réinitialiser avec un tableau vide en cas d'erreur
|
|
533
|
-
setStatus("error");
|
|
534
|
-
}
|
|
535
|
-
};
|
|
536
|
-
|
|
537
|
-
const deleteAliceAndBob = () => {
|
|
538
|
-
USER.DELETE_MANY({ names: ["Alice", "Bob"] });
|
|
539
|
-
};
|
|
540
|
-
|
|
541
|
-
return (
|
|
542
|
-
<div
|
|
543
|
-
style={{
|
|
544
|
-
border: "1px solid #dee2e6",
|
|
545
|
-
borderRadius: "8px",
|
|
546
|
-
padding: "16px",
|
|
547
|
-
margin: "16px 0",
|
|
548
|
-
backgroundColor: "#f8f9fa",
|
|
549
|
-
}}
|
|
550
|
-
>
|
|
551
|
-
<div
|
|
552
|
-
style={{
|
|
553
|
-
display: "flex",
|
|
554
|
-
justifyContent: "space-between",
|
|
555
|
-
alignItems: "center",
|
|
556
|
-
marginBottom: "12px",
|
|
557
|
-
}}
|
|
558
|
-
>
|
|
559
|
-
<h3 style={{ margin: 0 }}>📋 Liste de tous les utilisateurs</h3>
|
|
560
|
-
<button
|
|
561
|
-
onClick={loadAllUsers}
|
|
562
|
-
disabled={status === "loading"}
|
|
563
|
-
style={{
|
|
564
|
-
padding: "6px 12px",
|
|
565
|
-
backgroundColor: "#007bff",
|
|
566
|
-
color: "white",
|
|
567
|
-
border: "none",
|
|
568
|
-
borderRadius: "4px",
|
|
569
|
-
cursor: status === "loading" ? "not-allowed" : "pointer",
|
|
570
|
-
opacity: status === "loading" ? 0.6 : 1,
|
|
571
|
-
}}
|
|
572
|
-
>
|
|
573
|
-
🔄 Recharger la liste
|
|
574
|
-
</button>
|
|
575
|
-
<button
|
|
576
|
-
onClick={deleteAliceAndBob}
|
|
577
|
-
disabled={status === "loading"}
|
|
578
|
-
style={{
|
|
579
|
-
padding: "6px 12px",
|
|
580
|
-
backgroundColor: "#007bff",
|
|
581
|
-
color: "white",
|
|
582
|
-
border: "none",
|
|
583
|
-
borderRadius: "4px",
|
|
584
|
-
cursor: status === "loading" ? "not-allowed" : "pointer",
|
|
585
|
-
opacity: status === "loading" ? 0.6 : 1,
|
|
586
|
-
}}
|
|
587
|
-
>
|
|
588
|
-
Supprimer Alice et Bob
|
|
589
|
-
</button>
|
|
590
|
-
</div>
|
|
591
|
-
|
|
592
|
-
{status === "loading" && <div>Chargement de la liste...</div>}
|
|
593
|
-
{status === "error" && (
|
|
594
|
-
<div style={{ color: "#dc3545" }}>Erreur lors du chargement</div>
|
|
595
|
-
)}
|
|
596
|
-
|
|
597
|
-
{users && users.length > 0 && (
|
|
598
|
-
<div
|
|
599
|
-
style={{
|
|
600
|
-
backgroundColor: "white",
|
|
601
|
-
padding: "12px",
|
|
602
|
-
borderRadius: "4px",
|
|
603
|
-
fontFamily: "monospace",
|
|
604
|
-
}}
|
|
605
|
-
>
|
|
606
|
-
{users.map((user) => (
|
|
607
|
-
<div key={user.id} style={{ marginBottom: "4px" }}>
|
|
608
|
-
{user.name} (ID: {user.id}, Âge: {user.age})
|
|
609
|
-
</div>
|
|
610
|
-
))}
|
|
611
|
-
</div>
|
|
612
|
-
)}
|
|
613
|
-
</div>
|
|
614
|
-
);
|
|
615
|
-
};
|
|
616
|
-
|
|
617
|
-
const ActionForm = ({
|
|
618
|
-
title,
|
|
619
|
-
children,
|
|
620
|
-
onSubmit,
|
|
621
|
-
buttonText,
|
|
622
|
-
buttonColor = "#28a745",
|
|
623
|
-
}) => {
|
|
624
|
-
const [status, setStatus] = useState("idle");
|
|
625
|
-
const [message, setMessage] = useState("");
|
|
626
|
-
|
|
627
|
-
const handleSubmit = async (e) => {
|
|
628
|
-
e.preventDefault();
|
|
629
|
-
setStatus("loading");
|
|
630
|
-
setMessage("");
|
|
631
|
-
|
|
632
|
-
try {
|
|
633
|
-
await onSubmit();
|
|
634
|
-
setStatus("success");
|
|
635
|
-
setMessage("✅ Action réussie !");
|
|
636
|
-
setTimeout(() => {
|
|
637
|
-
setStatus("idle");
|
|
638
|
-
setMessage("");
|
|
639
|
-
}, 3000);
|
|
640
|
-
} catch (error) {
|
|
641
|
-
setStatus("error");
|
|
642
|
-
setMessage(`❌ Erreur: ${error.message}`);
|
|
643
|
-
setTimeout(() => {
|
|
644
|
-
setStatus("idle");
|
|
645
|
-
setMessage("");
|
|
646
|
-
}, 5000);
|
|
647
|
-
}
|
|
648
|
-
};
|
|
649
|
-
|
|
650
|
-
return (
|
|
651
|
-
<form
|
|
652
|
-
onSubmit={handleSubmit}
|
|
653
|
-
style={{
|
|
654
|
-
border: "1px solid #dee2e6",
|
|
655
|
-
borderRadius: "8px",
|
|
656
|
-
padding: "16px",
|
|
657
|
-
margin: "8px 0",
|
|
658
|
-
backgroundColor: "white",
|
|
659
|
-
}}
|
|
660
|
-
>
|
|
661
|
-
<h4 style={{ marginTop: 0, color: "#495057" }}>{title}</h4>
|
|
662
|
-
{children}
|
|
663
|
-
|
|
664
|
-
{message && (
|
|
665
|
-
<div
|
|
666
|
-
style={{
|
|
667
|
-
margin: "8px 0",
|
|
668
|
-
padding: "8px",
|
|
669
|
-
borderRadius: "4px",
|
|
670
|
-
backgroundColor: status === "success" ? "#d4edda" : "#f8d7da",
|
|
671
|
-
color: status === "success" ? "#155724" : "#721c24",
|
|
672
|
-
}}
|
|
673
|
-
>
|
|
674
|
-
{message}
|
|
675
|
-
</div>
|
|
676
|
-
)}
|
|
677
|
-
|
|
678
|
-
<button
|
|
679
|
-
type="submit"
|
|
680
|
-
disabled={status === "loading"}
|
|
681
|
-
style={{
|
|
682
|
-
padding: "8px 16px",
|
|
683
|
-
backgroundColor: buttonColor,
|
|
684
|
-
color: "white",
|
|
685
|
-
border: "none",
|
|
686
|
-
borderRadius: "4px",
|
|
687
|
-
cursor: status === "loading" ? "not-allowed" : "pointer",
|
|
688
|
-
opacity: status === "loading" ? 0.6 : 1,
|
|
689
|
-
marginTop: "8px",
|
|
690
|
-
}}
|
|
691
|
-
>
|
|
692
|
-
{status === "loading" ? "⏳ En cours..." : buttonText}
|
|
693
|
-
</button>
|
|
694
|
-
</form>
|
|
695
|
-
);
|
|
696
|
-
};
|
|
697
|
-
|
|
698
|
-
const App = () => {
|
|
699
|
-
const [newUserName, setNewUserName] = useState("TestUser");
|
|
700
|
-
const [newUserAge, setNewUserAge] = useState("28");
|
|
701
|
-
|
|
702
|
-
const createUser = async () => {
|
|
703
|
-
await USER.POST({ name: newUserName, age: parseInt(newUserAge) });
|
|
704
|
-
};
|
|
705
|
-
|
|
706
|
-
const runDeleteRecreateScenario = async () => {
|
|
707
|
-
console.log("🎯 Scénario: Supprimer puis recréer");
|
|
708
|
-
|
|
709
|
-
// 1. Supprimer Alice
|
|
710
|
-
await USER.DELETE({ name: "Alice" });
|
|
711
|
-
console.log("Étape 1: Alice supprimée");
|
|
712
|
-
|
|
713
|
-
// 2. Attendre un peu
|
|
714
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
715
|
-
|
|
716
|
-
// 3. Recréer Alice
|
|
717
|
-
await USER.POST({ name: "Alice", age: 31 });
|
|
718
|
-
console.log("Étape 2: Alice recréée");
|
|
719
|
-
};
|
|
720
|
-
|
|
721
|
-
const runRenameScenario = async () => {
|
|
722
|
-
console.log("🎯 Scénario: Renommer avec PUT");
|
|
723
|
-
|
|
724
|
-
// 1. Renommer Bob en TestUser
|
|
725
|
-
await USER.PUT({ name: "Bob", newName: "TestUser" });
|
|
726
|
-
console.log("Étape 1: Bob renommé en TestUser");
|
|
727
|
-
};
|
|
728
|
-
|
|
729
|
-
const runPatchRenameScenario = async () => {
|
|
730
|
-
console.log("🎯 Scénario: Renommer avec PATCH");
|
|
731
|
-
|
|
732
|
-
// 1. Renommer Bob en TestUser avec PATCH
|
|
733
|
-
await USER.PATCH({ name: "Bob", newName: "TestUser", age: 40 });
|
|
734
|
-
console.log("Étape 1: Bob renommé en TestUser avec PATCH");
|
|
735
|
-
};
|
|
736
|
-
|
|
737
|
-
const inputStyle = {
|
|
738
|
-
padding: "6px 10px",
|
|
739
|
-
border: "1px solid #ced4da",
|
|
740
|
-
borderRadius: "4px",
|
|
741
|
-
marginRight: "8px",
|
|
742
|
-
marginBottom: "8px",
|
|
743
|
-
};
|
|
744
|
-
|
|
745
|
-
return (
|
|
746
|
-
<div
|
|
747
|
-
style={{
|
|
748
|
-
maxWidth: "1200px",
|
|
749
|
-
margin: "0 auto",
|
|
750
|
-
padding: "20px",
|
|
751
|
-
fontFamily: "Arial, sans-serif",
|
|
752
|
-
}}
|
|
753
|
-
>
|
|
754
|
-
<h1
|
|
755
|
-
style={{
|
|
756
|
-
color: "#333",
|
|
757
|
-
borderBottom: "3px solid #007bff",
|
|
758
|
-
paddingBottom: "10px",
|
|
759
|
-
}}
|
|
760
|
-
>
|
|
761
|
-
🧪 Test MutableId Autoreload System
|
|
762
|
-
</h1>
|
|
763
|
-
|
|
764
|
-
<p
|
|
765
|
-
style={{
|
|
766
|
-
backgroundColor: "#e7f3ff",
|
|
767
|
-
padding: "15px",
|
|
768
|
-
borderRadius: "8px",
|
|
769
|
-
border: "1px solid #b3d9ff",
|
|
770
|
-
}}
|
|
771
|
-
>
|
|
772
|
-
Cette interface teste le système d'autoreload pour les ressources
|
|
773
|
-
avec mutableId. Observez comment les actions GET se rechargent
|
|
774
|
-
automatiquement après les modifications.
|
|
775
|
-
<br />
|
|
776
|
-
<strong>Astuce:</strong> Ouvrez la console pour voir les logs détaillés.
|
|
777
|
-
</p>
|
|
778
|
-
|
|
779
|
-
{/* Scénarios automatiques */}
|
|
780
|
-
<div
|
|
781
|
-
style={{
|
|
782
|
-
backgroundColor: "#f8f9fa",
|
|
783
|
-
padding: "16px",
|
|
784
|
-
borderRadius: "8px",
|
|
785
|
-
margin: "16px 0",
|
|
786
|
-
}}
|
|
787
|
-
>
|
|
788
|
-
<h2>🎯 Scénarios de test automatiques</h2>
|
|
789
|
-
<div style={{ display: "flex", gap: "10px", flexWrap: "wrap" }}>
|
|
790
|
-
<ActionForm
|
|
791
|
-
title=""
|
|
792
|
-
onSubmit={runDeleteRecreateScenario}
|
|
793
|
-
buttonText="🔄 Supprimer puis recréer Alice"
|
|
794
|
-
buttonColor="#6f42c1"
|
|
795
|
-
>
|
|
796
|
-
<p
|
|
797
|
-
style={{
|
|
798
|
-
margin: "0 0 8px 0",
|
|
799
|
-
fontSize: "0.9em",
|
|
800
|
-
color: "#6c757d",
|
|
801
|
-
}}
|
|
802
|
-
>
|
|
803
|
-
Teste l'autoreload POST avec mutableId
|
|
804
|
-
</p>
|
|
805
|
-
</ActionForm>
|
|
806
|
-
|
|
807
|
-
<ActionForm
|
|
808
|
-
title=""
|
|
809
|
-
onSubmit={runRenameScenario}
|
|
810
|
-
buttonText="✏️ PUT: Bob ↔ TestUser"
|
|
811
|
-
buttonColor="#6f42c1"
|
|
812
|
-
>
|
|
813
|
-
<p
|
|
814
|
-
style={{
|
|
815
|
-
margin: "0 0 8px 0",
|
|
816
|
-
fontSize: "0.9em",
|
|
817
|
-
color: "#6c757d",
|
|
818
|
-
}}
|
|
819
|
-
>
|
|
820
|
-
Teste l'autoreload PUT avec changement de mutableId
|
|
821
|
-
</p>
|
|
822
|
-
</ActionForm>
|
|
823
|
-
|
|
824
|
-
<ActionForm
|
|
825
|
-
title=""
|
|
826
|
-
onSubmit={runPatchRenameScenario}
|
|
827
|
-
buttonText="📝 PATCH: Bob ↔ TestUser"
|
|
828
|
-
buttonColor="#6f42c1"
|
|
829
|
-
>
|
|
830
|
-
<p
|
|
831
|
-
style={{
|
|
832
|
-
margin: "0 0 8px 0",
|
|
833
|
-
fontSize: "0.9em",
|
|
834
|
-
color: "#6c757d",
|
|
835
|
-
}}
|
|
836
|
-
>
|
|
837
|
-
Teste l'autoreload PATCH avec changement de mutableId
|
|
838
|
-
</p>
|
|
839
|
-
</ActionForm>
|
|
840
|
-
</div>
|
|
841
|
-
</div>
|
|
842
|
-
|
|
843
|
-
{/* Liste des utilisateurs */}
|
|
844
|
-
<UsersList />
|
|
845
|
-
|
|
846
|
-
{/* Actions GET individuelles */}
|
|
847
|
-
<div
|
|
848
|
-
style={{
|
|
849
|
-
backgroundColor: "#f8f9fa",
|
|
850
|
-
padding: "16px",
|
|
851
|
-
borderRadius: "8px",
|
|
852
|
-
margin: "16px 0",
|
|
853
|
-
}}
|
|
854
|
-
>
|
|
855
|
-
<h2>👥 Actions GET individuelles avec CRUD</h2>
|
|
856
|
-
<p style={{ color: "#6c757d", fontSize: "0.9em" }}>
|
|
857
|
-
Ces actions GET devraient se recharger automatiquement quand les
|
|
858
|
-
ressources correspondantes sont modifiées. Chaque carte permet aussi
|
|
859
|
-
de modifier ou supprimer l'utilisateur.
|
|
860
|
-
</p>
|
|
861
|
-
|
|
862
|
-
<div
|
|
863
|
-
style={{
|
|
864
|
-
display: "grid",
|
|
865
|
-
gridTemplateColumns: "repeat(auto-fit, minmax(300px, 1fr))",
|
|
866
|
-
gap: "16px",
|
|
867
|
-
}}
|
|
868
|
-
>
|
|
869
|
-
<UserCard name="Alice" action={aliceAction} />
|
|
870
|
-
<UserCard name="TestUser" action={testUserAction} />
|
|
871
|
-
<UserCard name="Bob" action={bobAction} />
|
|
872
|
-
<UserCard name="Charlie" action={charlieAction} />
|
|
873
|
-
</div>
|
|
874
|
-
</div>
|
|
875
|
-
|
|
876
|
-
{/* Création d'utilisateurs */}
|
|
877
|
-
<div
|
|
878
|
-
style={{
|
|
879
|
-
backgroundColor: "#f8f9fa",
|
|
880
|
-
padding: "16px",
|
|
881
|
-
borderRadius: "8px",
|
|
882
|
-
margin: "16px 0",
|
|
883
|
-
}}
|
|
884
|
-
>
|
|
885
|
-
<h2>➕ Créer un nouvel utilisateur</h2>
|
|
886
|
-
|
|
887
|
-
<ActionForm
|
|
888
|
-
title="Créer un utilisateur"
|
|
889
|
-
onSubmit={createUser}
|
|
890
|
-
buttonText="Créer"
|
|
891
|
-
buttonColor="#28a745"
|
|
892
|
-
>
|
|
893
|
-
<div>
|
|
894
|
-
<input
|
|
895
|
-
type="text"
|
|
896
|
-
placeholder="Nom"
|
|
897
|
-
value={newUserName}
|
|
898
|
-
onChange={(e) => setNewUserName(e.target.value)}
|
|
899
|
-
style={inputStyle}
|
|
900
|
-
/>
|
|
901
|
-
<input
|
|
902
|
-
type="number"
|
|
903
|
-
placeholder="Âge"
|
|
904
|
-
value={newUserAge}
|
|
905
|
-
onChange={(e) => setNewUserAge(e.target.value)}
|
|
906
|
-
style={inputStyle}
|
|
907
|
-
/>
|
|
908
|
-
</div>
|
|
909
|
-
</ActionForm>
|
|
910
|
-
</div>
|
|
911
|
-
|
|
912
|
-
<div
|
|
913
|
-
style={{
|
|
914
|
-
backgroundColor: "#fff3cd",
|
|
915
|
-
border: "1px solid #ffeaa7",
|
|
916
|
-
borderRadius: "8px",
|
|
917
|
-
padding: "16px",
|
|
918
|
-
margin: "16px 0",
|
|
919
|
-
}}
|
|
920
|
-
>
|
|
921
|
-
<h3 style={{ marginTop: 0 }}>💡 Instructions de test</h3>
|
|
922
|
-
<ol style={{ marginBottom: 0 }}>
|
|
923
|
-
<li>
|
|
924
|
-
<strong>Chargez d'abord les utilisateurs</strong> avec les
|
|
925
|
-
boutons "Recharger"
|
|
926
|
-
</li>
|
|
927
|
-
<li>
|
|
928
|
-
<strong>Observez les statuts</strong> : Vert = chargé, Jaune =
|
|
929
|
-
chargement, Rouge avec 404 = utilisateur inexistant
|
|
930
|
-
</li>
|
|
931
|
-
<li>
|
|
932
|
-
<strong>Testez l'autoreload</strong> :
|
|
933
|
-
<ul>
|
|
934
|
-
<li>
|
|
935
|
-
Créez "TestUser" → l'action GET
|
|
936
|
-
"TestUser" devrait passer de 404 à vert
|
|
937
|
-
automatiquement
|
|
938
|
-
</li>
|
|
939
|
-
<li>
|
|
940
|
-
Supprimez puis recréez Alice → l'action GET
|
|
941
|
-
"Alice" devrait se recharger
|
|
942
|
-
</li>
|
|
943
|
-
<li>
|
|
944
|
-
Modifiez un utilisateur directement depuis sa carte →
|
|
945
|
-
l'affichage devrait se mettre à jour automatiquement
|
|
946
|
-
</li>
|
|
947
|
-
</ul>
|
|
948
|
-
</li>
|
|
949
|
-
<li>
|
|
950
|
-
<strong>Actions dans les cartes</strong> : Chaque utilisateur a des
|
|
951
|
-
boutons Modifier et Supprimer. Le mode édition permet de changer le
|
|
952
|
-
nom, l'âge, ou les deux.
|
|
953
|
-
</li>
|
|
954
|
-
<li>
|
|
955
|
-
<strong>Vérifiez les logs</strong> dans la console pour voir les
|
|
956
|
-
détails des autoreloads
|
|
957
|
-
</li>
|
|
958
|
-
</ol>
|
|
959
|
-
</div>
|
|
960
|
-
</div>
|
|
961
|
-
);
|
|
962
|
-
};
|
|
963
|
-
|
|
964
|
-
render(<App />, document.querySelector("#root"));
|