diff --git a/app/Http/Controllers/AttributeController.php b/app/Http/Controllers/AttributeController.php new file mode 100644 index 0000000..7941bb3 --- /dev/null +++ b/app/Http/Controllers/AttributeController.php @@ -0,0 +1,55 @@ + AttributeType::with('attributes')->get(), + ]); + } + + public function create() + { + return Inertia::render('Attributes/Edit', [ + 'attributeTypes' => AttributeType::all(), + ]); + } + + public function store(Request $request) + { + $id = Attribute::create($request->all())->id; + return to_route('attributes.edit',$id); + } + + public function show($id) + { + } + + public function edit($id) + { + $attribute = Attribute::findOrFail($id); + return Inertia::render('Attributes/Edit', [ + 'attributeTypes' => AttributeType::all(), + 'attribute' => $attribute, + ]); + } + + public function update(Request $request, $id) + { + $attribute = Attribute::findOrFail($id); + $attribute->update($request->all()); + return to_route('attributes.index'); + } + + public function destroy($id) + { + } +} diff --git a/app/Http/Controllers/AttributeTypeController.php b/app/Http/Controllers/AttributeTypeController.php index f409291..6caee81 100644 --- a/app/Http/Controllers/AttributeTypeController.php +++ b/app/Http/Controllers/AttributeTypeController.php @@ -11,18 +11,20 @@ class AttributeTypeController extends Controller { public function index() { - return Inertia::render('AttributeType/Index', [ - 'attributeTypes' => AttributeType::all(), + return Inertia::render('AttributeTypes/Index', [ + 'attributeTypes' => AttributeType::with('attributes')->get(), ]); } public function create() { - return Inertia::render('Attributes/Edit', ['colors' => AttributeType::$colors]); + return Inertia::render('AttributeTypes/Edit', ['colors' => AttributeType::$colors]); } public function store(Request $request) { + $id = AttributeType::create($request->all())->id; + return to_route('attribute_types.edit',$id); } public function show($id) @@ -31,10 +33,16 @@ public function show($id) public function edit($id) { + $attributeType = AttributeType::findOrFail($id); + return Inertia::render('AttributeTypes/Edit', + ['colors' => AttributeType::$colors, 'attributeType' => $attributeType]); } public function update(Request $request, $id) { + $attributeType = AttributeType::findOrFail($id); + $attributeType->update($request->all()); + return to_route('attribute_types.index'); } public function destroy($id) diff --git a/app/Http/Controllers/ItemController.php b/app/Http/Controllers/ItemController.php index cbcd401..49fe283 100644 --- a/app/Http/Controllers/ItemController.php +++ b/app/Http/Controllers/ItemController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers; use App\Http\Requests\StoreItemRequest; +use App\Models\Attribute; use App\Models\AttributeType; use App\Models\Item; use Illuminate\Http\Request; @@ -39,7 +40,10 @@ public function index(Request $request) */ public function create() { - return Inertia::render('Items/Edit', []); + return Inertia::render('Items/Edit', [ + 'items' => Item::query()->get(), + 'attributeTypes' => AttributeType::with('attributes')->get(), + ]); } /** @@ -51,9 +55,12 @@ public function store(StoreItemRequest $request) $photo?->storeAs('photos/' . $photo->hashName(), ['disk' => 'public']); $wiringPhoto = $request->file('wiring_photo'); $wiringPhoto?->storeAs('photos/' . $photo->hashName(), ['disk' => 'public']); - $item = Item::create($request->all()); + + $item = Item::create($request->except('attributes', 'photo', 'wiring_photo')); + $item->attributes()->sync(explode(',', $request->input('attributes'))); $item->photo = $photo?->hashName(); $item->wiring_photo = $wiringPhoto?->hashName(); + $item->save(); return redirect(route('items.index')); } @@ -75,7 +82,18 @@ public function show(string $publicId) public function edit(int $id) { return Inertia::render('Items/Edit', [ - 'item' => Item::findorFail($id), + 'item' => Item::with('attributes')->findorFail($id), + 'items' => Item::query()->select('id', 'title')->get(), + 'attributeTypes' => AttributeType::with('attributes')->get(), + 'myAttributes' => AttributeType::whereHas('attributes', function ($attributes) use ($id) { + $attributes->whereHas('items', function ($query) use ($id) { + $query->where('items.id', $id); + }); + })->with(['attributes' => function ($attributes) use ($id) { + $attributes->whereHas('items', function ($query) use ($id) { + $query->where('items.id', $id); + }); + }])->get(), ]); } @@ -85,23 +103,31 @@ public function edit(int $id) public function update(StoreItemRequest $request, int $id) { $item = Item::findOrFail($id); - $photo = $request->file('photo'); - $photo->storeAs('photos/' . $photo->hashName(), ['disk' => 'public']); - $photo = $request->file('wiring_photo'); - $photo->storeAs('photos/' . $photo->hashName(), ['disk' => 'public']); + $photo = $request->file('photo'); + if($photo){ + $photo->storeAs('photos/' . $photo->hashName(), ['disk' => 'public']); - if ($item->photo) { - //delete the old photo - Storage::disk('public')->delete('photos/' . $item->photo); + if ($item->photo) { + //delete the old photo + Storage::disk('public')->delete('photos/' . $item->photo); + } + $item->photo = $photo->hashName(); } - if ($item->wiring_photo) { - //delete the old photo - Storage::disk('public')->delete('photos/' . $item->wiring_photo); + + $wiring_photo = $request->file('wiring_photo'); + if($wiring_photo){ + $wiring_photo->storeAs('photos/' . $wiring_photo->hashName(), ['disk' => 'public']); + + if ($item->wiring_photo) { + //delete the old photo + Storage::disk('public')->delete('photos/' . $item->wiring_photo); + } + $item->wiring_photo = $wiring_photo->hashName(); } - $item->update($request->all()); - $item->photo = $photo->hashName(); - $item->wiring_photo = $photo->hashName(); + + $item->update($request->except('attributes', 'photo', 'wiring_photo')); + $item->attributes()->sync(explode(',', $request->input('attributes'))); $item->save(); return redirect()->back(); } @@ -115,6 +141,12 @@ public function destroy(Item $item) //delete the old photo Storage::disk('public')->delete('photos/' . $item->photo); } + if ($item->wiring_photo) { + //delete the old photo + Storage::disk('public')->delete('photos/' . $item->wiring_photo); + } + $item->attributes()->detach(); + $item->edges()->delete(); $item->delete(); } } diff --git a/app/Models/Attribute.php b/app/Models/Attribute.php index 6dc8315..619b960 100644 --- a/app/Models/Attribute.php +++ b/app/Models/Attribute.php @@ -8,12 +8,14 @@ use Illuminate\Database\Eloquent\Relations\BelongsToMany; /** - * @property string name + * @property string title * @property string description */ class Attribute extends Model { use HasFactory; + + protected $fillable = ['title', 'description', 'attribute_type_id']; public function items(): BelongsToMany { return $this->belongsToMany(Item::class); diff --git a/app/Models/Edge.php b/app/Models/Edge.php new file mode 100644 index 0000000..1340ab0 --- /dev/null +++ b/app/Models/Edge.php @@ -0,0 +1,33 @@ +belongsTo(Attribute::class, 'from_item_id'); + } + + public function toItem(): BelongsTo + { + return $this->belongsTo(Attribute::class, 'to_item_id'); + } + + public function belongsToItem(): BelongsTo + { + return $this->belongsTo(Attribute::class, 'belongsto_item_id'); + } +} diff --git a/app/Models/Item.php b/app/Models/Item.php index 5fa75b0..6569728 100644 --- a/app/Models/Item.php +++ b/app/Models/Item.php @@ -3,6 +3,7 @@ namespace App\Models; use Illuminate\Database\Eloquent\Relations\BelongsToMany; +use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Support\HigherOrderCollectionProxy; use Vinkla\Hashids\Facades\Hashids; use Illuminate\Database\Eloquent\Factories\HasFactory; @@ -51,6 +52,11 @@ public function attributes(): BelongsToMany return $this->belongsToMany(Attribute::class); } + public function edges(): HasMany + { + return $this->hasMany(Edge::class); + } + public function getPhotoUrlAttribute(): string { return asset('storage/photos/' . $this->photo); diff --git a/database/factories/EdgeFactory.php b/database/factories/EdgeFactory.php new file mode 100644 index 0000000..057af88 --- /dev/null +++ b/database/factories/EdgeFactory.php @@ -0,0 +1,24 @@ + Carbon::now(), + 'updated_at' => Carbon::now(), + + 'from_attribute_id' => Attribute::factory(), + 'to_attribute_id' => Attribute::factory(), + ]; + } +} diff --git a/database/migrations/2024_06_09_223050_create_edges_table.php b/database/migrations/2024_06_09_223050_create_edges_table.php new file mode 100644 index 0000000..e8aff39 --- /dev/null +++ b/database/migrations/2024_06_09_223050_create_edges_table.php @@ -0,0 +1,23 @@ +id(); + $table->foreignId('from_item_id'); + $table->foreignId('to_item_id'); + $table->foreignId('belongsto_item_id'); + $table->timestamps(); + }); + } + + public function down(): void + { + Schema::dropIfExists('edges'); + } +}; diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index 18b97c2..f4d6218 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -4,6 +4,7 @@ use App\Models\Attribute; use App\Models\AttributeType; +use App\Models\Edge; use App\Models\Item; use App\Models\Processor; use App\Models\User; @@ -31,25 +32,48 @@ public function run(): void 'email' => 'test@example.com', ]); + $processors = AttributeType::create( + [ + 'title' => 'Controllers', + 'description' => 'All the controllers you can use to interact with the sensors and actuators', + 'color' => 'blue', + ] + ); + + Attribute::create( + [ + 'attribute_type_id' => $processors->id, + 'title' => 'Arduino', + 'description' => 'A microcontroller that can be used to interact with sensors and actuators', + ] + ); + + Attribute::create( + [ + 'attribute_type_id' => $processors->id, + 'title' => 'ESP8266', + 'description' => 'The ESP8266 is a low-cost Wi-Fi microchip with full TCP/IP stack and microcontroller capability produced by Espressif Systems', + ] + ); + AttributeType::factory(10)->has( Attribute::factory()->count(5) )->create(); - Item::factory(10)->create(); - foreach (Item::all() as $item) { + $items = Item::factory(10)->create(); + $attributes = Attribute::all(); + foreach ($items as $item) { $item->attributes()->attach( - Attribute::all()->random(3) + $attributes->random(3) ); } - - Processor::create([ - 'name' => 'Laptop', - 'description' => 'A portable computer', - ]); - - - - + for($i = 0; $i < sizeof($items) - 1; $i++) { + Edge::create([ + 'from_item_id' => $items->get($i)->id, + 'to_item_id' => $items->get($i+1)->id, + 'belongsto_item_id' => $items->first()->id, + ]); + } } } diff --git a/package-lock.json b/package-lock.json index 41d0eb2..e0af84e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,6 +5,8 @@ "packages": { "": { "dependencies": { + "@highlightjs/vue-plugin": "^2.1.0", + "highlight.js": "^11.9.0", "marked": "^12.0.2", "qrcode.vue": "^3.4.1", "v-network-graph": "^0.9.15" @@ -520,6 +522,15 @@ "node": ">=12" } }, + "node_modules/@highlightjs/vue-plugin": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@highlightjs/vue-plugin/-/vue-plugin-2.1.0.tgz", + "integrity": "sha512-E+bmk4ncca+hBEYRV2a+1aIzIV0VSY/e5ArjpuSN9IO7wBJrzUE2u4ESCwrbQD7sAy+jWQjkV5qCCWgc+pu7CQ==", + "peerDependencies": { + "highlight.js": "^11.0.1", + "vue": "^3" + } + }, "node_modules/@inertiajs/core": { "version": "1.0.16", "resolved": "https://registry.npmjs.org/@inertiajs/core/-/core-1.0.16.tgz", @@ -1873,6 +1884,14 @@ "he": "bin/he" } }, + "node_modules/highlight.js": { + "version": "11.9.0", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.9.0.tgz", + "integrity": "sha512-fJ7cW7fQGCYAkgv4CPfwFHrfd/cLS4Hau96JuJ+ZTOWhjnhoeN1ub1tFmALm/+lW5z4WCAuAV9bm05AP0mS6Gw==", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", diff --git a/package.json b/package.json index 149d196..ec5a0a2 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,8 @@ "vue-tsc": "^1.8.27" }, "dependencies": { + "@highlightjs/vue-plugin": "^2.1.0", + "highlight.js": "^11.9.0", "marked": "^12.0.2", "qrcode.vue": "^3.4.1", "v-network-graph": "^0.9.15" diff --git a/resources/js/Components/Card.vue b/resources/js/Components/Card.vue new file mode 100644 index 0000000..271182a --- /dev/null +++ b/resources/js/Components/Card.vue @@ -0,0 +1,13 @@ + + diff --git a/resources/js/CustomComponents/AttributeFilter.vue b/resources/js/CustomComponents/AttributeFilter.vue new file mode 100644 index 0000000..08a33c4 --- /dev/null +++ b/resources/js/CustomComponents/AttributeFilter.vue @@ -0,0 +1,82 @@ + + diff --git a/resources/js/CustomComponents/OpeningCard.vue b/resources/js/CustomComponents/OpeningCard.vue index 607ee29..1165383 100644 --- a/resources/js/CustomComponents/OpeningCard.vue +++ b/resources/js/CustomComponents/OpeningCard.vue @@ -4,12 +4,13 @@ import OpeningComponent from "@/CustomComponents/OpeningComponent.vue"; const props = defineProps<{ title: string; + open?: boolean; }>();