Skip to content

Commit

Permalink
Merge pull request #132 from semanadeinformatica/feature/shop
Browse files Browse the repository at this point in the history
Shop
  • Loading branch information
toni-santos authored Sep 30, 2023
2 parents a3d2a63 + 4ed2a84 commit c5489b9
Show file tree
Hide file tree
Showing 15 changed files with 277 additions and 14 deletions.
32 changes: 32 additions & 0 deletions app/Http/Controllers/ProductCRUDController.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class ProductCRUDController extends CRUDController
'price' => 'required|integer',
'stock' => 'required|integer',
'edition_id' => 'required|exists:editions,id',
'image' => 'nullable|mimes:jpg,jpeg,png|max:1024',
];

protected array $search = ['name', 'price', 'stock'];
Expand All @@ -26,4 +27,35 @@ protected function with(): array
'editions' => Edition::all(),
];
}

protected function created(array $new): ?array
{
$product = Product::create([
'name' => $new['name'],
'price' => $new['price'],
'stock' => $new['stock'],
'edition_id' => $new['edition_id'],
]);

if (isset($new['image'])) {
$product->updateImageProduct($new['image']);
}

return null;
}

protected function updated($old, array $new): ?array
{

if (isset($new['image'])) {
$old->updateImageProduct($new['image']);
}

return [
'name' => $new['name'],
'price' => $new['price'],
'stock' => $new['stock'],
'edition_id' => $new['edition_id'],
];
}
}
22 changes: 22 additions & 0 deletions app/Http/Controllers/ShopController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Inertia\Inertia;

class ShopController extends Controller
{
public function show(Request $request)
{
$edition = $request->input('edition');

if ($edition === null) {
return response('No edition found', 500);
}

return Inertia::render('Shop', [
'products' => $edition->products,
]);
}
}
7 changes: 7 additions & 0 deletions app/Models/Product.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace App\Models;

use App\Traits\HasImageProduct;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
Expand All @@ -10,6 +11,7 @@
class Product extends Model
{
use HasFactory;
use HasImageProduct;

/**
* The attributes that are mass assignable.
Expand All @@ -21,6 +23,11 @@ class Product extends Model
'price',
'stock',
'edition_id',
'image_path',
];

protected $appends = [
'image_product_url',
];

public function edition(): BelongsTo
Expand Down
2 changes: 1 addition & 1 deletion app/Traits/HasCV.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public function updateCV(UploadedFile $cv, $storagePath = 'cvs')
*/
public function deleteCV()
{
//missing check to verify if user can manage CVs
//#TODO: missing check to verify if user can manage CVs

if (is_null($this->cv_path)) {
return;
Expand Down
70 changes: 70 additions & 0 deletions app/Traits/HasImageProduct.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?php

namespace App\Traits;

use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;

trait HasImageProduct
{
/**
* Update the user's CV.
*
* @param string $storagePath
* @return void
*/
public function updateImageProduct(UploadedFile $image, $storagePath = 'product-images')
{

tap($this->image_path, function ($previous) use ($image, $storagePath) {
if ($previous) {
Storage::disk($this->ImageProductDisk())->delete($previous);
}

$this->forceFill([
'image_path' => $image->storePublicly(
$storagePath, ['disk' => $this->ImageProductDisk()]
),
])->save();
});
}

/**
* Delete the user's CV.
*
* @return void
*/
public function deleteImageProduct()
{
if (is_null($this->image_path)) {
return;
}

Storage::disk($this->ImageProductDisk())->delete($this->image_path);

$this->forceFill([
'image_path' => null,
])->save();
}

/**
* Get the URL to the user's CV.
*/
public function ImageProductUrl(): Attribute
{
return Attribute::get(function () {
return Storage::disk($this->ImageProductDisk())->url($this->image_path);
});
}

/**
* Get the disk that CVs should be stored on.
*
* @return string
*/
protected function ImageProductDisk()
{
return config('jetstream.product_image_disk', 'public');
}
}
1 change: 1 addition & 0 deletions config/jetstream.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,5 +78,6 @@

'profile_photo_disk' => 'public',
'cv_disk' => 'public',
'product_image_disk' => 'public',

];
28 changes: 28 additions & 0 deletions database/migrations/2023_09_27_131517_add_product_image.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('products', function (Blueprint $table) {
$table->string('image_path', 2048)->nullable();
});
}

/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('products', function (Blueprint $table) {
$table->dropColumn('image_path');
});
}
};
18 changes: 7 additions & 11 deletions resources/js/Components/HamburgerMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import route, {
type QueryParams,
type RouteParamsWithQueryOverload,
} from "ziggy-js";
import type Competition from "@/Types/Competition";
interface Route {
label: string;
Expand All @@ -17,7 +18,7 @@ const open = ref(false);
const props = defineProps<{
options: {
pages: Routes;
activities: Routes;
competitions: Competition[];
editions: number[];
};
}>();
Expand Down Expand Up @@ -78,21 +79,16 @@ watch(open, () => {
</div>
<section class="flex flex-col items-center pt-6">
<h2 class="pb-3 text-center font-bold text-2023-orange">
Atividades
Competições
</h2>
<template
v-for="({ label, _query }, page) in props.options.activities"
:key="page"
v-for="competition in props.options.competitions"
:key="competition.id"
>
<ResponsiveNavLink
:href="
route(route().has(page) ? page : 'home', {
_query,
} as RouteParamsWithQueryOverload)
"
:active="page === route().current()"
:href="route('competition.show', { competition })"
>
{{ label }}
{{ competition.name }}
</ResponsiveNavLink>
</template>
</section>
Expand Down
3 changes: 2 additions & 1 deletion resources/js/Components/Navbar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const pageRoutes: Routes = {
day: 1,
},
},
shop: { label: "Loja" },
team: { label: "Equipa" },
// sponsors: { label: "Patrocínios" },
// contacts: { label: "Contactos" },
Expand All @@ -53,7 +54,7 @@ const editionRoutes = [2022, 2021, 2020, 2019, 2018];
const options = {
pages: pageRoutes,
activities: activityRoutes,
competitions: usePage().props.competitions,
editions: editionRoutes,
};
Expand Down
51 changes: 51 additions & 0 deletions resources/js/Components/Shop/ShopItem.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<script setup lang="ts">
import type Product from "@/Types/Product";
import PrimaryButton from "@/Components/PrimaryButton.vue";
import { ref } from "vue";
import { VueFinalModal } from "vue-final-modal";
import "vue-final-modal/style.css";
const options = ref({
modelValue: false,
});
interface Props {
product: Product;
}
defineProps<Props>();
</script>

<template>
<div class="flex max-w-[20em] flex-col border-2 border-black">
<img
class="h-[20em] max-w-[20em] border-b-2 border-black object-cover"
:src="product.image_product_url"
/>
<div
class="flex cursor-pointer flex-col bg-2023-orange px-4 py-2 text-white"
@click="options.modelValue = true"
>
<h2 class="text-xl font-bold">{{ product.name }}</h2>
<div class="flex flex-row gap-2 self-end text-xl">
<p>{{ product.price }}</p>
<img class="w-5" src="/images/cy-sinf-small.svg" />
</div>
</div>
</div>
<VueFinalModal
v-model="options.modelValue"
class="flex items-center justify-center"
content-class="max-w-xl mx-4 p-4 bg-2023-bg border border-black border-solid flex relative justify-center items-center flex-col gap-8"
>
<img
:src="product.image_product_url"
class="w-auto justify-center border border-solid border-black shadow-xl shadow-2023-teal-dark"
/>
<p>
Confirmar compra de <b>{{ product.name }}</b> por
{{ product.price }}?
</p>
<PrimaryButton> Comprar </PrimaryButton>
</VueFinalModal>
</template>
10 changes: 10 additions & 0 deletions resources/js/Pages/CRUD/Product/Create.vue
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<script setup lang="ts">
import ImageInput from "@/Components/ImageInput.vue";
import PrimaryButton from "@/Components/PrimaryButton.vue";
import TextInput from "@/Components/TextInput.vue";
import CardLayout from "@/Layouts/CardLayout.vue";
Expand All @@ -19,6 +20,7 @@ const form = useForm({
price: "",
stock: "",
edition_id: "",
image: null as File | null,
});
const submit = () => {
Expand All @@ -29,6 +31,14 @@ const submit = () => {
<template>
<CardLayout title="Criar produto">
<form class="contents" @submit.prevent="submit">
<ImageInput
id="image"
v-model="form.image"
label="Imagem do produto"
class="self-stretch"
:error-message="form.errors.image"
/>

<TextInput
id="name"
v-model="form.name"
Expand Down
14 changes: 13 additions & 1 deletion resources/js/Pages/CRUD/Product/Edit.vue
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<script setup lang="ts">
import ImageInput from "@/Components/ImageInput.vue";
import PrimaryButton from "@/Components/PrimaryButton.vue";
import TextInput from "@/Components/TextInput.vue";
import CardLayout from "@/Layouts/CardLayout.vue";
Expand All @@ -17,20 +18,31 @@ interface Props {
const { item: product } = defineProps<Props>();
const form = useForm({
_method: "PUT",
name: product.name,
price: product.price.toString(),
stock: product.stock.toString(),
edition_id: product.edition_id.toString(),
image: null as File | null,
});
const submit = () => {
form.put(route("admin.products.update", product));
form.post(route("admin.products.update", product));
};
</script>

<template>
<CardLayout title="Editar produto">
<form class="contents" @submit.prevent="submit">
<ImageInput
id="image"
v-model="form.image"
:initial-preview="item.image_product_url"
label="Imagem do produto"
class="self-stretch"
:error-message="form.errors.image"
/>

<TextInput
id="name"
v-model="form.name"
Expand Down
Loading

0 comments on commit c5489b9

Please sign in to comment.