diff --git a/timeliner/app/Http/Controllers/TimelineController.php b/timeliner/app/Http/Controllers/TimelineController.php index 20b58b8..ade6778 100644 --- a/timeliner/app/Http/Controllers/TimelineController.php +++ b/timeliner/app/Http/Controllers/TimelineController.php @@ -3,7 +3,14 @@ namespace App\Http\Controllers; use App\Models\Timeline; +use App\Models\Ownership; +use App\Models\Comment; +use App\Models\Node; use Illuminate\Http\Request; +use Illuminate\Support\Facades\Auth; +use Illuminate\Support\Facades\Log; +use Illuminate\View\View; + class TimelineController extends Controller { @@ -18,6 +25,32 @@ public function timelinelist() return view('timeline.timelinelist'); } + public function show($id) + { + $timeline = Timeline::findOrFail($id); + + if(($timeline != null) && (!$timeline->private || (Auth::check() && Ownership::find($timeline->id . Auth::user()->id)))) + { + $nodes = Node::where('timeline','=',$timeline->id) + ->with('milestones') + ->get(); + + Log::info('nodes: '.$nodes->count()); + $comments = Comment::where('timeline', '=', $timeline->id); + + $isOwner = false; + if (Auth::check()) + { + $isOwner = Ownership::find($timeline->id . Auth::user()->id); + } + + return view('timeline.timeline', ['isOwner'=> $isOwner, 'timeline' => $timeline, 'nodes' => $nodes]); + } + + return redirect()->route('timeline.index') + ->with('success',"You don't have access."); + } + public function create() { return view('timeline.create'); @@ -31,7 +64,8 @@ public function store(Request $request) 'private' => 'required|boolean' ]); - Timeline::create($request->all()); + $timeline = Timeline::create($request->all()); + Ownership::create(['id' => $timeline->id . Auth::user()->id]); return redirect()->route('timeline.index') ->with('success','Timeline created successfully.'); diff --git a/timeliner/app/Models/Node.php b/timeliner/app/Models/Node.php index 08d1925..93fe507 100644 --- a/timeliner/app/Models/Node.php +++ b/timeliner/app/Models/Node.php @@ -8,4 +8,9 @@ class Node extends Model { use HasFactory; + + public function milestones() + { + return $this->hasMany(Milestone::class, 'node'); + } } diff --git a/timeliner/app/Models/Ownership.php b/timeliner/app/Models/Ownership.php index 5ae8d7f..c267d71 100644 --- a/timeliner/app/Models/Ownership.php +++ b/timeliner/app/Models/Ownership.php @@ -8,4 +8,9 @@ class Ownership extends Model { use HasFactory; + + protected $fillable = [ + 'id', + ]; + } diff --git a/timeliner/database/migrations/2024_10_09_194426_create_timelines_table.php b/timeliner/database/migrations/2024_10_09_194426_create_timelines_table.php index 2742f04..84a486c 100644 --- a/timeliner/database/migrations/2024_10_09_194426_create_timelines_table.php +++ b/timeliner/database/migrations/2024_10_09_194426_create_timelines_table.php @@ -12,7 +12,7 @@ public function up(): void { Schema::create('timelines', function (Blueprint $table) { - $table->id(); + $table->id('id'); $table->string('name'); $table->boolean('private'); $table->string('description'); diff --git a/timeliner/database/migrations/2024_10_09_194444_create_nodes_table.php b/timeliner/database/migrations/2024_10_09_194444_create_nodes_table.php index 4decab2..6b174bf 100644 --- a/timeliner/database/migrations/2024_10_09_194444_create_nodes_table.php +++ b/timeliner/database/migrations/2024_10_09_194444_create_nodes_table.php @@ -12,7 +12,13 @@ public function up(): void { Schema::create('nodes', function (Blueprint $table) { - $table->id(); + $table->id('id'); + $table->string('name'); + $table->string('color'); + $table->integer('timeline'); + + $table->foreign('timeline')->references('id')->on('timelines')->onDelete('cascade'); + $table->timestamps(); }); } diff --git a/timeliner/database/migrations/2024_10_09_194452_create_milestones_table.php b/timeliner/database/migrations/2024_10_09_194452_create_milestones_table.php index 1808e04..17b8374 100644 --- a/timeliner/database/migrations/2024_10_09_194452_create_milestones_table.php +++ b/timeliner/database/migrations/2024_10_09_194452_create_milestones_table.php @@ -13,6 +13,12 @@ public function up(): void { Schema::create('milestones', function (Blueprint $table) { $table->id(); + $table->string('description'); + $table->date('date'); + $table->integer('node'); + + $table->foreign('node')->references('id')->on('nodes')->onDelete('cascade'); + $table->timestamps(); }); } diff --git a/timeliner/database/migrations/2024_10_09_194510_create_ownerships_table.php b/timeliner/database/migrations/2024_10_09_194510_create_ownerships_table.php index 82d0862..bf26a94 100644 --- a/timeliner/database/migrations/2024_10_09_194510_create_ownerships_table.php +++ b/timeliner/database/migrations/2024_10_09_194510_create_ownerships_table.php @@ -12,7 +12,7 @@ public function up(): void { Schema::create('ownerships', function (Blueprint $table) { - $table->id(); + $table->string('id')->primary(); $table->timestamps(); }); } diff --git a/timeliner/database/seeders/DatabaseSeeder.php b/timeliner/database/seeders/DatabaseSeeder.php index 0de0f1e..0eff136 100644 --- a/timeliner/database/seeders/DatabaseSeeder.php +++ b/timeliner/database/seeders/DatabaseSeeder.php @@ -3,7 +3,7 @@ namespace Database\Seeders; use App\Models\User; -use App\Models\Timeline; +use App\Models\Ownership; use Illuminate\Support\Facades\DB; // use Illuminate\Database\Console\Seeds\WithoutModelEvents; use Illuminate\Database\Seeder; @@ -15,8 +15,6 @@ class DatabaseSeeder extends Seeder */ public function run(): void { - // User::factory(10)->create(); - DB::table('users')->truncate(); User::factory()->create([ @@ -25,6 +23,18 @@ public function run(): void 'password' => '12345' ]); + User::factory()->create([ + 'name' => 'Other User', + 'email' => 'test2@example.com', + 'password' => '12345' + ]); + + Ownership::create([ + 'id' => '22', + ]); + $this->call(TimelineSeeder::class); + $this->call(NodeSeeder::class); + $this->call(MilestoneSeeder::class); } } diff --git a/timeliner/database/seeders/MilestoneSeeder.php b/timeliner/database/seeders/MilestoneSeeder.php new file mode 100644 index 0000000..85f80ac --- /dev/null +++ b/timeliner/database/seeders/MilestoneSeeder.php @@ -0,0 +1,36 @@ + "Summer camp gathering", 'date' => Carbon::parse('2000-01-01'), 'node' => "1"], + ['description' => "Bach play competition", 'date' => Carbon::parse('2002-08-01'), 'node' => "1"], + ['description' => "Bach play competition", 'date' => Carbon::parse('2002-08-01'), 'node' => "2"], + ['description' => "Lima concerto", 'date' => Carbon::parse('2002-10-01'), 'node' => "2"], + ['description' => "Anne started practicing", 'date' => Carbon::parse('2013-07-07'), 'node' => "3"], + ]; + + foreach ($milestones as $milestone) { + Milestone::create(array( + 'description' => $milestone['description'], + 'date' => $milestone['date'], + 'node' => $milestone['node'] + )); + } + } +} diff --git a/timeliner/database/seeders/NodeSeeder.php b/timeliner/database/seeders/NodeSeeder.php new file mode 100644 index 0000000..833cd44 --- /dev/null +++ b/timeliner/database/seeders/NodeSeeder.php @@ -0,0 +1,34 @@ + "Jules' concerts", 'color' => '#ffc0cb', 'timeline' => "1"], + ['name' => "Anne's concerts", 'color' => '#bdb76b', 'timeline' => "1"], + ['name' => "Violin", 'color' => '#40e0d0', 'timeline' => "2"], + ['name' => "Piano", 'color' => '#49796b', 'timeline' => "2"], + ]; + + foreach ($nodes as $node) { + Node::create(array( + 'name' => $node['name'], + 'color' => $node['color'], + 'timeline' => $node['timeline'] + )); + } + } +} diff --git a/timeliner/resources/css/timelinestyle.css b/timeliner/resources/css/timelinestyle.css new file mode 100644 index 0000000..5e6a776 --- /dev/null +++ b/timeliner/resources/css/timelinestyle.css @@ -0,0 +1,85 @@ +.timeline +{ + position: relative; + z-index: 90; +} + +.node +{ + background-image: linear-gradient(to top, transparent 40%, var(--line-color) 10%, transparent 50%); + background-repeat: repeat-x; + background-position-y: center; + position: relative; +} + +.milestones-container { + + display: flex; + align-items: center; +} + +.milestone { + flex-shrink: 0; + display: inline-block; + cursor: pointer; + position: relative; +} + +.milestone-icon { + width: 50px; + height: 50px; + border-radius: 50%; + background-color: #313131; + color: white; + display: flex; + justify-content: center; + align-items: center; + font-size: 24px; + transition: transform 0.3s ease; +} + +.milestone-icon:hover { + transform: scale(1.2); +} + +/* Hidden content - info panel */ +.milestone-info { + display: none; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + max-width: 250px; + width: auto; + z-index: 100; +} + +/* When the milestone is hovered, display the info */ +.milestone:hover .milestone-info { + display: block; +} + +/* Styling for the content panel */ +.milestone-content { + background-color: white; + border-radius: 8px; + padding: 10px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + opacity: 0; + animation: fadeIn 0.3s forwards; +} + +/* Optional: Fade-in animation */ +@keyframes fadeIn { + from { + opacity: 0; + transform: translateY(10px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + + + diff --git a/timeliner/resources/js/timelinelistener.js b/timeliner/resources/js/timelinelistener.js new file mode 100644 index 0000000..ebcd273 --- /dev/null +++ b/timeliner/resources/js/timelinelistener.js @@ -0,0 +1,58 @@ +document.addEventListener('DOMContentLoaded', () => { + const nodes = document.querySelectorAll('.node'); + const dates = []; + + // Gather all milestone dates for range calculation + nodes.forEach(node => { + const milestones = node.querySelectorAll('.milestone'); + milestones.forEach(milestone => { + const date = milestone.dataset.date; + if (date) dates.push(parseDate(date)); + }); + }); + + if (dates.length === 0) { + console.warn("No milestone dates found."); + return; + } + + // Calculate date range for proportional spacing + const minDate = new Date(Math.min(...dates)); + const maxDate = new Date(Math.max(...dates)); + + console.log("mindate " + minDate); + console.log("maxdate " + maxDate); + + nodes.forEach(node => { + const milestones = node.querySelectorAll('.milestone'); + + const totalDays = (maxDate - minDate) / (1000 * 60 * 60 * 24); + + + milestones.forEach((milestone, index) => { + let prevMilestoneDate; + if (index === 0) { + prevMilestoneDate = minDate; + } + else{ + prevMilestoneDate = parseDate(milestones[index - 1].dataset.date); + } + const currentMilestoneDate = parseDate(milestone.dataset.date); + + const daysDifference = (currentMilestoneDate - prevMilestoneDate) / (1000 * 60 * 60 * 24); + const gapPercentage = (daysDifference / totalDays) * 100; + + console.log("milestones length: " + milestones.length); + console.log(prevMilestoneDate + " percentage is " + gapPercentage +"index"+index); + milestone.style.marginLeft = `${gapPercentage}%`; + + + }); + }); +}); + + +const parseDate = (dateTimeString) => { + const datePart = dateTimeString.split(' ')[0]; + return new Date(datePart); +}; diff --git a/timeliner/resources/views/timeline/partials/milestone.blade.php b/timeliner/resources/views/timeline/partials/milestone.blade.php index e69de29..64288c1 100644 --- a/timeliner/resources/views/timeline/partials/milestone.blade.php +++ b/timeliner/resources/views/timeline/partials/milestone.blade.php @@ -0,0 +1,11 @@ +
{{ $milestone->date }}
+{{$timeline->description}}
- View + View diff --git a/timeliner/resources/views/timeline/timeline.blade.php b/timeliner/resources/views/timeline/timeline.blade.php index 9cd419c..4fa9d64 100644 --- a/timeliner/resources/views/timeline/timeline.blade.php +++ b/timeliner/resources/views/timeline/timeline.blade.php @@ -1,5 +1,47 @@ -