diff --git a/timeliner/app/Http/Controllers/TimelineController.php b/timeliner/app/Http/Controllers/TimelineController.php
index c5b5771..03738de 100644
--- a/timeliner/app/Http/Controllers/TimelineController.php
+++ b/timeliner/app/Http/Controllers/TimelineController.php
@@ -79,7 +79,7 @@ public function store(Request $request)
'nodes.*.milestones.*.description' => 'nullable|string|max:255',
]);
- $timeline = Timeline::create(['name' => $validated['name'], 'description' => $validated['description'], 'private'=> $validated['private']]);
+ $timeline = Timeline::create(['name' => $validated['name'], 'description' => $validated['description'], 'private'=> !$validated['private']]);
Ownership::create(['id' => $timeline->id . Auth::user()->id]);
if (!empty($validated['nodes'])) {
@@ -106,4 +106,107 @@ public function store(Request $request)
return redirect()->route('timeline.index')
->with('success','Timeline created successfully.');
}
+
+ public function edit($id)
+ {
+ $timeline = Timeline::findOrFail($id);
+
+ if(($timeline != null) && (Auth::check() && Ownership::find($timeline->id . Auth::user()->id)))
+ {
+ $nodes = Node::where('timeline','=',$timeline->id)
+ ->with('milestones')
+ ->get();
+
+ return view('timeline.edit', ['timeline' => $timeline, 'nodes' => $nodes]);
+ }
+
+ return redirect()->route('timeline.show', $id)
+ ->withErrors(["You don't have access."]);
+ }
+
+ public function update(Request $request, $id)
+ {
+ $timeline = Timeline::findOrFail($id);
+
+ if (($timeline != null) && (!$timeline->private || (Auth::check() && Ownership::find($timeline->id . Auth::user()->id)))) {
+
+ $request->merge([
+ 'private' => $request->has('private'),
+ ]);
+
+ $validated = $request->validate([
+ 'name' => 'required|max:50',
+ 'description' => 'required|max:200',
+ 'private' => 'required|boolean',
+ 'nodes' => 'nullable|array',
+ 'nodes.*.name' => 'required|string|max:255',
+ 'nodes.*.milestones' => 'nullable|array',
+ 'nodes.*.milestones.*.date' => 'required|date',
+ 'nodes.*.milestones.*.description' => 'nullable|string|max:255',
+ ]);
+
+ // Update timeline details
+ $timeline->update([
+ 'name' => $validated['name'],
+ 'description' => $validated['description'],
+ 'private' => !$validated['private']
+ ]);
+
+ // Handle nodes
+ if (!empty($validated['nodes'])) {
+ // First, delete any nodes that were removed from the form
+ $existingNodeIds = collect($validated['nodes'])->pluck('id')->filter();
+ Node::where('timeline', $timeline->id)
+ ->whereNotIn('id', $existingNodeIds)
+ ->delete();
+
+ // Update existing nodes or create new ones
+ foreach ($validated['nodes'] as $nodeData) {
+ if (isset($nodeData['id'])) {
+ // Update existing node
+ $node = Node::findOrFail($nodeData['id']);
+ $node->update([
+ 'name' => $nodeData['name'],
+ 'color' => '#FFFFFF', // Default color (you can adjust this)
+ ]);
+ } else {
+ // Create new node
+ $node = Node::create([
+ 'name' => $nodeData['name'],
+ 'color' => '#FFFFFF', // Default color (you can adjust this)
+ 'timeline' => $timeline->id,
+ ]);
+ }
+
+ // Handle milestones
+ if (!empty($nodeData['milestones'])) {
+ foreach ($nodeData['milestones'] as $milestoneData) {
+ if (isset($milestoneData['id'])) {
+ // Update existing milestone
+ $milestone = Milestone::findOrFail($milestoneData['id']);
+ $milestone->update([
+ 'date' => $milestoneData['date'],
+ 'description' => $milestoneData['description'] ?? null,
+ ]);
+ } else {
+ // Create new milestone
+ Milestone::create([
+ 'date' => $milestoneData['date'],
+ 'description' => $milestoneData['description'] ?? null,
+ 'node' => $node->id
+ ]);
+ }
+ }
+ }
+ }
+ }
+
+ return redirect()->route('timeline.show', $id)
+ ->with('success', 'Timeline updated successfully.');
+ }
+
+ return redirect()->route('timeline.index')
+ ->withErrors(["You don't have access."]);
+ }
+
}
diff --git a/timeliner/database/seeders/DatabaseSeeder.php b/timeliner/database/seeders/DatabaseSeeder.php
index 4de21dc..0db30ed 100644
--- a/timeliner/database/seeders/DatabaseSeeder.php
+++ b/timeliner/database/seeders/DatabaseSeeder.php
@@ -44,7 +44,5 @@ public function run(): void
$this->call(CommentSeeder::class);
$this->call(NodeSeeder::class);
$this->call(MilestoneSeeder::class);
-
- DB::statement('PRAGMA foreign_keys=ON;');
}
}
diff --git a/timeliner/resources/js/app.js b/timeliner/resources/js/app.js
index 630cc78..79d4f1a 100644
--- a/timeliner/resources/js/app.js
+++ b/timeliner/resources/js/app.js
@@ -4,7 +4,7 @@ import './timelinelistener';
import './formfunctions';
import './createformfunctions';
-import './bootstrap-toggle.min.js'
+import './bootstrap-toggle.min'
import Alpine from 'alpinejs';
diff --git a/timeliner/resources/js/createformfunctions.js b/timeliner/resources/js/createformfunctions.js
index 79fd1de..4e0d469 100644
--- a/timeliner/resources/js/createformfunctions.js
+++ b/timeliner/resources/js/createformfunctions.js
@@ -3,9 +3,11 @@ Author: Brandt Mael
*/
-let ni = 0; // node index
+let ni = window.ni || 0; // use global ni if set, else defaults to 0
let mi = 0; // milestone index
+window.addMilestone = addMilestone; // makes the function available to the global scope
+
//add a row to a list of milestone creation form
function addMilestone(milestone_list_id, nodeIndex, nodeMilestoneCount) {
const ms_id = `ms-${nodeIndex}-${nodeMilestoneCount}` // the id of the milestone depend of it's parent node
@@ -43,10 +45,10 @@ function addMilestone(milestone_list_id, nodeIndex, nodeMilestoneCount) {
// create delete button
let delete_button = document.createElement('button');
let delete_button_id = "ms-delete-button-" + mi;
- delete_button.setAttribute("class", "btn btn-primary");
+ delete_button.setAttribute("class", "btn btn-danger bi bi-trash");
delete_button.setAttribute("type", "button");
delete_button.setAttribute("id", delete_button_id);
- delete_button.innerHTML += 'delete';
+ delete_button.innerHTML += ' delete';
// add listener to delete_button
delete_button.addEventListener('click', function() {
@@ -78,10 +80,10 @@ if (nodeCreateButton) {
nodeCreateButton.addEventListener('click', function() {
// recover the node table
- let node_table = document.getElementById('node-list');
+ let node_table = document.getElementById('node-list-body');
// declare new row of milestone table
- let tr_node_form = document.createElement("div"); // row holding the node form
+ let tr_node_form = document.createElement("tr"); // row holding the node form
let tr_milestone_table = document.createElement("tr"); // row holding the milestone table
let td_label = document.createElement("td");
@@ -129,10 +131,10 @@ if (nodeCreateButton) {
// create 'add milestone' button
let add_milestone_button = document.createElement('button');
let milestone_create_button_id = "milestone-create-button-" + ni;
- add_milestone_button.setAttribute("class", "btn btn-primary");
+ add_milestone_button.setAttribute("class", "btn btn-primary bi bi-calendar-plus");
add_milestone_button.setAttribute("type", "button");
add_milestone_button.setAttribute("id", milestone_create_button_id);
- add_milestone_button.innerHTML += 'create milestone';
+ add_milestone_button.innerHTML += ' create milestone';
let current_ni_number = ni;
let nodeMilestoneCount = 0;
@@ -145,10 +147,10 @@ if (nodeCreateButton) {
// create delete button
let delete_button = document.createElement('button');
let delete_button_id = "node-delete-button-" + ni;
- delete_button.setAttribute("class", "btn btn-primary");
+ delete_button.setAttribute("class", "btn btn-danger bi bi-trash ");
delete_button.setAttribute("type", "button");
delete_button.setAttribute("id", delete_button_id);
- delete_button.innerHTML += 'delete';
+ delete_button.innerHTML += ' delete';
// add listener to delete_button
delete_button.addEventListener('click', function() {
diff --git a/timeliner/resources/views/index.blade.php b/timeliner/resources/views/index.blade.php
index 03260d4..19442d1 100644
--- a/timeliner/resources/views/index.blade.php
+++ b/timeliner/resources/views/index.blade.php
@@ -5,8 +5,6 @@
-
-
diff --git a/timeliner/resources/views/layouts/app.blade.php b/timeliner/resources/views/layouts/app.blade.php
index 7109ed6..018dbbb 100644
--- a/timeliner/resources/views/layouts/app.blade.php
+++ b/timeliner/resources/views/layouts/app.blade.php
@@ -32,6 +32,8 @@
@endisset
+
+
{{ $slot }}
diff --git a/timeliner/resources/views/layouts/navigation.blade.php b/timeliner/resources/views/layouts/navigation.blade.php
index e3f91db..03abd2a 100644
--- a/timeliner/resources/views/layouts/navigation.blade.php
+++ b/timeliner/resources/views/layouts/navigation.blade.php
@@ -29,7 +29,7 @@
-
+
{{ __('Create timeline') }}
diff --git a/timeliner/resources/views/timeline/createtimeline.blade.php b/timeliner/resources/views/timeline/create.blade.php
similarity index 84%
rename from timeliner/resources/views/timeline/createtimeline.blade.php
rename to timeliner/resources/views/timeline/create.blade.php
index df27423..dbb0e97 100644
--- a/timeliner/resources/views/timeline/createtimeline.blade.php
+++ b/timeliner/resources/views/timeline/create.blade.php
@@ -44,29 +44,16 @@
-
+
-
- @if ($errors->any())
-
-
Oops! There's a problem with your entries.
-
- @foreach ($errors->all() as $error)
- - {{ $error }}
- @endforeach
-
-
- @endif
-
-
diff --git a/timeliner/resources/views/timeline/edit.blade.php b/timeliner/resources/views/timeline/edit.blade.php
new file mode 100644
index 0000000..325aa94
--- /dev/null
+++ b/timeliner/resources/views/timeline/edit.blade.php
@@ -0,0 +1,57 @@
+
+
+
+ {{ __('Edit timeline ') . $timeline->name }}
+
+
+
+
+
\ No newline at end of file
diff --git a/timeliner/resources/views/timeline/partials/nodecreate.blade.php b/timeliner/resources/views/timeline/partials/nodecreate.blade.php
index 78e575e..9c992b5 100644
--- a/timeliner/resources/views/timeline/partials/nodecreate.blade.php
+++ b/timeliner/resources/views/timeline/partials/nodecreate.blade.php
@@ -1,16 +1,86 @@
+
@csrf
diff --git a/timeliner/resources/views/timeline/partials/nodeedit.blade.php b/timeliner/resources/views/timeline/partials/nodeedit.blade.php
index e69de29..cb73cf9 100644
--- a/timeliner/resources/views/timeline/partials/nodeedit.blade.php
+++ b/timeliner/resources/views/timeline/partials/nodeedit.blade.php
@@ -0,0 +1,82 @@
+
diff --git a/timeliner/resources/views/timeline/timeline.blade.php b/timeliner/resources/views/timeline/timeline.blade.php
index 9034b62..29c1e39 100644
--- a/timeliner/resources/views/timeline/timeline.blade.php
+++ b/timeliner/resources/views/timeline/timeline.blade.php
@@ -5,22 +5,19 @@
-
-
@auth
- @if ($isOwner)
Edit @endif
+ @if ($isOwner)
Edit @endif
@endauth
-
@foreach ($nodes as $node)
@include("timeline.partials.node", ["node"=>$node])
@endforeach
diff --git a/timeliner/routes/web.php b/timeliner/routes/web.php
index 8adfc91..52aabef 100644
--- a/timeliner/routes/web.php
+++ b/timeliner/routes/web.php
@@ -15,9 +15,7 @@
return view('dashboard');
})->middleware(['auth', 'verified'])->name('dashboard');
-Route::get('/createtimeline', function () {
- return view('timeline.createtimeline');
-})->middleware(['auth', 'verified'])->name('createtimeline');
+
Route::middleware('auth')->group(function () {
Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit');
@@ -27,8 +25,8 @@
require __DIR__.'/auth.php';
-Route::resource('timeline', TimelineController::class);
+Route::resource('timeline', TimelineController::class)->middleware(['auth', 'verified']);
-Route::post('comment', [CommentController::class, 'store'])->name('comment.store');
-Route::delete('comment/{comment}', [CommentController::class, 'destroy'])->name('comment.destroy');
-Route::put('comment/{comment}', [CommentController::class, 'update'])->name('comment.update');
\ No newline at end of file
+Route::post('comment', [CommentController::class, 'store'])->middleware(['auth', 'verified'])->name('comment.store');
+Route::delete('comment/{comment}', [CommentController::class, 'destroy'])->middleware(['auth', 'verified'])->name('comment.destroy');
+Route::put('comment/{comment}', [CommentController::class, 'update'])->middleware(['auth', 'verified'])->name('comment.update');
\ No newline at end of file