feat(Notification & membership route)
This commit is contained in:
@@ -27,7 +27,7 @@ class ContactFormController extends Controller
|
||||
{
|
||||
$validated = $request->validated();
|
||||
try {
|
||||
$contact = $this->contactService->registerNewContactRequest($validated);
|
||||
$this->contactService->registerNewContactRequest($validated);
|
||||
} catch (\Throwable $e) {
|
||||
\Log::error('Erreur lors de la création d\'un contact', [
|
||||
'error' => $e->getMessage(),
|
||||
|
||||
@@ -4,13 +4,11 @@ namespace App\Http\Controllers\Forms;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Forms\MembershipRequest;
|
||||
use App\Models\Member;
|
||||
use App\Models\Membership;
|
||||
use App\Models\Package;
|
||||
use App\Services\MemberService;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Inertia\Inertia;
|
||||
|
||||
class MembershipFormController extends Controller
|
||||
@@ -25,7 +23,7 @@ class MembershipFormController extends Controller
|
||||
return Inertia::render('forms/membership', [
|
||||
'plans' => Package::query()
|
||||
->where('is_active', true)
|
||||
->select('id', 'name', 'price', 'description')
|
||||
->select('id', 'identifier', 'name', 'price', 'description')
|
||||
->get()
|
||||
]);
|
||||
}
|
||||
@@ -36,6 +34,7 @@ class MembershipFormController extends Controller
|
||||
*/
|
||||
public function store(MembershipRequest $request): RedirectResponse
|
||||
{
|
||||
dd($request->validated());
|
||||
$validated = $request->validated();
|
||||
|
||||
try {
|
||||
@@ -49,11 +48,11 @@ class MembershipFormController extends Controller
|
||||
|
||||
return redirect()
|
||||
->route('membership')
|
||||
->with('error', __('memberships.subscription.error'));
|
||||
->with('error', Membership::getAttributeLabel('memberships.subscription.error'));
|
||||
}
|
||||
|
||||
return redirect()
|
||||
->route('membership')
|
||||
->with('success', __('memberships.subscription.success'));
|
||||
->with('success', Membership::getAttributeLabel('memberships.subscription.success'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ class ContactRequest extends FormRequest
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,7 +11,7 @@ class MembershipRequest extends FormRequest
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -26,12 +26,11 @@ class MembershipRequest extends FormRequest
|
||||
'lastname' => 'required|string|max:255',
|
||||
'firstname' => 'required|string|max:255',
|
||||
'email' => 'required|email|max:255',
|
||||
'company' => 'required|string|max:255',
|
||||
'company' => 'string|max:255',
|
||||
'address' => 'required|string|max:255',
|
||||
'zipcode' => 'required|string|max:255',
|
||||
'city' => 'required|string|max:255',
|
||||
'phone1' => 'required|string|max:255',
|
||||
'group_id' => 'required|string|max:255',
|
||||
|
||||
// Membership
|
||||
'package' => 'required|string|max:255',
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace App\Listeners;
|
||||
|
||||
use App\Events\MemberRegistered;
|
||||
use App\Models\User;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
|
||||
@@ -21,6 +22,9 @@ class NotifyAdminForMembershipRequest
|
||||
*/
|
||||
public function handle(MemberRegistered $event): void
|
||||
{
|
||||
//
|
||||
$admin = User::where('name', 'SuperAdmin')->first();
|
||||
|
||||
$admin->notify(new AdminNewUserPending($event->user));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
|
||||
/**
|
||||
* @property int $id
|
||||
@@ -63,7 +64,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
*/
|
||||
class Member extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
use HasFactory, Notifiable;
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'keycloak_id',
|
||||
|
||||
@@ -59,7 +59,7 @@ class Membership extends Model
|
||||
|
||||
public static function getAttributeLabel(string $attribute): string
|
||||
{
|
||||
return __("membership.fields.$attribute");
|
||||
return __("memberships.fields.$attribute");
|
||||
}
|
||||
|
||||
public function member(): BelongsTo
|
||||
|
||||
@@ -17,5 +17,9 @@ class ContactService
|
||||
$contact->fill($data);
|
||||
$contact->save();
|
||||
|
||||
// Envoyer un email à l'administrateur
|
||||
|
||||
return $contact;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Events\MemberRegistered;
|
||||
use App\Models\Member;
|
||||
use App\Models\MemberGroup;
|
||||
use App\Models\Package;
|
||||
@@ -29,7 +30,6 @@ class MemberService
|
||||
$member->firstname = $data['firstname'];
|
||||
$member->email = $data['email'];
|
||||
$member->company = $data['company'] ?? null;
|
||||
$member->date_of_birth = Carbon::parse($data['date_of_birth'])->format('Y-m-d H:i:s') ?? null;
|
||||
$member->address = $data['address'];
|
||||
$member->zipcode = $data['zipcode'];
|
||||
$member->city = $data['city'];
|
||||
@@ -38,7 +38,7 @@ class MemberService
|
||||
$member->save();
|
||||
}
|
||||
|
||||
$package = Package::where('id', $data['package_id'])
|
||||
$package = Package::where('identifier', $data['package'])
|
||||
->where('is_active', true)
|
||||
->firstOrFail();
|
||||
|
||||
@@ -47,11 +47,13 @@ class MemberService
|
||||
'status' => 'pending',
|
||||
'package_id' => $package->id ?? null,
|
||||
'amount' => $data['amount'],
|
||||
'payment_status' => 'pending',
|
||||
'payment_status' => 'unpaid',
|
||||
|
||||
]);
|
||||
|
||||
// Notify Admin
|
||||
$admin = Member::where('role', 'admin')->first();
|
||||
event(new MemberRegistered($admin));
|
||||
|
||||
|
||||
return $member;
|
||||
|
||||
@@ -18,7 +18,7 @@ return [
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('LOG_CHANNEL', 'stack'),
|
||||
'default' => env('LOG_CHANNEL', 'daily'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
@@ -126,7 +126,12 @@ return [
|
||||
'emergency' => [
|
||||
'path' => storage_path('logs/laravel.log'),
|
||||
],
|
||||
|
||||
'query' => [
|
||||
'driver' => 'daily',
|
||||
'path' => storage_path('logs/db/query.log'),
|
||||
'level' => 'debug',
|
||||
'days' => 3,
|
||||
],
|
||||
],
|
||||
|
||||
];
|
||||
|
||||
@@ -16,7 +16,7 @@ return new class extends Migration
|
||||
$table->foreignId('member_id')->constrained('members')->onDelete('cascade');
|
||||
$table->foreignId('admin_id')->nullable()->constrained('users')->onDelete('set null');
|
||||
$table->foreignId('package_id')->constrained('packages')->onDelete('cascade');
|
||||
$table->date('start_date');
|
||||
$table->date('start_date')->nullable();
|
||||
$table->date('end_date')->nullable();
|
||||
$table->enum('status', ['active', 'expired', 'pending'])->default('pending');
|
||||
$table->decimal('amount', 10, 2)->default(0);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { queryParams, type RouteQueryOptions, type RouteDefinition, type RouteFormDefinition } from './../../../../../wayfinder'
|
||||
/**
|
||||
* @see \App\Http\Controllers\Forms\MembershipFormController::create
|
||||
* @see app/Http/Controllers/Forms/MembershipFormController.php:23
|
||||
* @see app/Http/Controllers/Forms/MembershipFormController.php:21
|
||||
* @route '/membership'
|
||||
*/
|
||||
export const create = (options?: RouteQueryOptions): RouteDefinition<'get'> => ({
|
||||
@@ -16,7 +16,7 @@ create.definition = {
|
||||
|
||||
/**
|
||||
* @see \App\Http\Controllers\Forms\MembershipFormController::create
|
||||
* @see app/Http/Controllers/Forms/MembershipFormController.php:23
|
||||
* @see app/Http/Controllers/Forms/MembershipFormController.php:21
|
||||
* @route '/membership'
|
||||
*/
|
||||
create.url = (options?: RouteQueryOptions) => {
|
||||
@@ -25,7 +25,7 @@ create.url = (options?: RouteQueryOptions) => {
|
||||
|
||||
/**
|
||||
* @see \App\Http\Controllers\Forms\MembershipFormController::create
|
||||
* @see app/Http/Controllers/Forms/MembershipFormController.php:23
|
||||
* @see app/Http/Controllers/Forms/MembershipFormController.php:21
|
||||
* @route '/membership'
|
||||
*/
|
||||
create.get = (options?: RouteQueryOptions): RouteDefinition<'get'> => ({
|
||||
@@ -35,7 +35,7 @@ create.get = (options?: RouteQueryOptions): RouteDefinition<'get'> => ({
|
||||
|
||||
/**
|
||||
* @see \App\Http\Controllers\Forms\MembershipFormController::create
|
||||
* @see app/Http/Controllers/Forms/MembershipFormController.php:23
|
||||
* @see app/Http/Controllers/Forms/MembershipFormController.php:21
|
||||
* @route '/membership'
|
||||
*/
|
||||
create.head = (options?: RouteQueryOptions): RouteDefinition<'head'> => ({
|
||||
@@ -45,7 +45,7 @@ create.head = (options?: RouteQueryOptions): RouteDefinition<'head'> => ({
|
||||
|
||||
/**
|
||||
* @see \App\Http\Controllers\Forms\MembershipFormController::create
|
||||
* @see app/Http/Controllers/Forms/MembershipFormController.php:23
|
||||
* @see app/Http/Controllers/Forms/MembershipFormController.php:21
|
||||
* @route '/membership'
|
||||
*/
|
||||
const createForm = (options?: RouteQueryOptions): RouteFormDefinition<'get'> => ({
|
||||
@@ -55,7 +55,7 @@ const createForm = (options?: RouteQueryOptions): RouteFormDefinition<'get'> =>
|
||||
|
||||
/**
|
||||
* @see \App\Http\Controllers\Forms\MembershipFormController::create
|
||||
* @see app/Http/Controllers/Forms/MembershipFormController.php:23
|
||||
* @see app/Http/Controllers/Forms/MembershipFormController.php:21
|
||||
* @route '/membership'
|
||||
*/
|
||||
createForm.get = (options?: RouteQueryOptions): RouteFormDefinition<'get'> => ({
|
||||
@@ -65,7 +65,7 @@ createForm.get = (options?: RouteQueryOptions): RouteFormDefinition<'get'> => ({
|
||||
|
||||
/**
|
||||
* @see \App\Http\Controllers\Forms\MembershipFormController::create
|
||||
* @see app/Http/Controllers/Forms/MembershipFormController.php:23
|
||||
* @see app/Http/Controllers/Forms/MembershipFormController.php:21
|
||||
* @route '/membership'
|
||||
*/
|
||||
createForm.head = (options?: RouteQueryOptions): RouteFormDefinition<'get'> => ({
|
||||
@@ -82,7 +82,7 @@ create.form = createForm
|
||||
|
||||
/**
|
||||
* @see \App\Http\Controllers\Forms\MembershipFormController::store
|
||||
* @see app/Http/Controllers/Forms/MembershipFormController.php:37
|
||||
* @see app/Http/Controllers/Forms/MembershipFormController.php:35
|
||||
* @route '/membership'
|
||||
*/
|
||||
export const store = (options?: RouteQueryOptions): RouteDefinition<'post'> => ({
|
||||
@@ -97,7 +97,7 @@ store.definition = {
|
||||
|
||||
/**
|
||||
* @see \App\Http\Controllers\Forms\MembershipFormController::store
|
||||
* @see app/Http/Controllers/Forms/MembershipFormController.php:37
|
||||
* @see app/Http/Controllers/Forms/MembershipFormController.php:35
|
||||
* @route '/membership'
|
||||
*/
|
||||
store.url = (options?: RouteQueryOptions) => {
|
||||
@@ -106,7 +106,7 @@ store.url = (options?: RouteQueryOptions) => {
|
||||
|
||||
/**
|
||||
* @see \App\Http\Controllers\Forms\MembershipFormController::store
|
||||
* @see app/Http/Controllers/Forms/MembershipFormController.php:37
|
||||
* @see app/Http/Controllers/Forms/MembershipFormController.php:35
|
||||
* @route '/membership'
|
||||
*/
|
||||
store.post = (options?: RouteQueryOptions): RouteDefinition<'post'> => ({
|
||||
@@ -116,7 +116,7 @@ store.post = (options?: RouteQueryOptions): RouteDefinition<'post'> => ({
|
||||
|
||||
/**
|
||||
* @see \App\Http\Controllers\Forms\MembershipFormController::store
|
||||
* @see app/Http/Controllers/Forms/MembershipFormController.php:37
|
||||
* @see app/Http/Controllers/Forms/MembershipFormController.php:35
|
||||
* @route '/membership'
|
||||
*/
|
||||
const storeForm = (options?: RouteQueryOptions): RouteFormDefinition<'post'> => ({
|
||||
@@ -126,7 +126,7 @@ const storeForm = (options?: RouteQueryOptions): RouteFormDefinition<'post'> =>
|
||||
|
||||
/**
|
||||
* @see \App\Http\Controllers\Forms\MembershipFormController::store
|
||||
* @see app/Http/Controllers/Forms/MembershipFormController.php:37
|
||||
* @see app/Http/Controllers/Forms/MembershipFormController.php:35
|
||||
* @route '/membership'
|
||||
*/
|
||||
storeForm.post = (options?: RouteQueryOptions): RouteFormDefinition<'post'> => ({
|
||||
|
||||
41
resources/js/components/flash-message.tsx
Normal file
41
resources/js/components/flash-message.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import {FlashMessages} from '@/types';
|
||||
import {Alert, AlertDescription} from '@/components/ui/alert';
|
||||
import {CheckCircle2, AlertCircle, AlertTriangle, Info} from 'lucide-react';
|
||||
|
||||
interface FlashMessageProps {
|
||||
messages: FlashMessages;
|
||||
}
|
||||
|
||||
export function FlashMessage({messages}: FlashMessageProps) {
|
||||
if (!messages || Object.keys(messages).length === 0) return null;
|
||||
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
{messages.success && (
|
||||
<Alert className="border-green-500 bg-green-50 text-green-900">
|
||||
<CheckCircle2 className="h-4 w-4 text-green-600"/>
|
||||
<AlertDescription>{messages.success}</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
{messages.error && (
|
||||
<Alert className="border-red-500 bg-red-50 text-red-900">
|
||||
<AlertCircle className="h-4 w-4 text-red-600"/>
|
||||
<AlertDescription>{messages.error}</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
{messages.warning && (
|
||||
<Alert className="border-yellow-500 bg-yellow-50 text-yellow-900">
|
||||
<AlertTriangle className="h-4 w-4 text-yellow-600"/>
|
||||
<AlertDescription>{messages.warning}</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
{messages.info && (
|
||||
<Alert className="border-blue-500 bg-blue-50 text-blue-900">
|
||||
<Info className="h-4 w-4 text-blue-600"/>
|
||||
<AlertDescription>{messages.info}</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import {useEffect, useState} from "react";
|
||||
import {Form, Head, usePage} from "@inertiajs/react";
|
||||
import {Alert, AlertDescription, AlertTitle} from "@/components/ui/alert";
|
||||
import {CheckCircle2} from "lucide-react";
|
||||
import {LoaderCircle} from 'lucide-react';
|
||||
import ContactFormController from "@/actions/App/Http/Controllers/Forms/ContactFormController";
|
||||
import {Label} from "@/components/ui/label";
|
||||
@@ -19,28 +17,31 @@ import {
|
||||
} from "@/components/ui/select";
|
||||
import {Textarea} from "@/components/ui/textarea";
|
||||
import NavGuestLayout from "@/layouts/nav-guest-layout";
|
||||
import {PageProps} from "@/types";
|
||||
import {FlashMessage} from "@/components/flash-message";
|
||||
|
||||
export default function Contact() {
|
||||
const {flash} = usePage().props;
|
||||
const {flash} = usePage().props as PageProps;
|
||||
|
||||
const [showSuccess, setShowSuccess] = useState(!!flash?.success);
|
||||
const [showFlashMessage, setFlashMessage] = useState(!!flash);
|
||||
|
||||
useEffect(() => {
|
||||
if (flash?.success) {
|
||||
setShowSuccess(true);
|
||||
const timer = setTimeout(() => setShowSuccess(false), 5000);
|
||||
if (flash) {
|
||||
setFlashMessage(true);
|
||||
const timer = setTimeout(() => setFlashMessage(false), 5000);
|
||||
return () => clearTimeout(timer);
|
||||
}
|
||||
}, [flash]);
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head title="Nous contacter"/>
|
||||
<div
|
||||
className="flex min-h-screen flex-col items-center bg-[#F5F5F5] p-6 text-[#1b1b18] lg:justify-center lg:p-8 dark:bg-[#0a0a0a]">
|
||||
className="flex flex-col items-center bg-[#F5F5F5] p-6 text-[#1b1b18] lg:justify-center lg:p-8 dark:bg-[#0a0a0a]">
|
||||
<NavGuestLayout/>
|
||||
|
||||
<div className="flex flex-col items-center justify-center gap-4">
|
||||
<section className="flex flex-col h-screen items-center mt-15 gap-4">
|
||||
<div>
|
||||
<h1>Nous contacter</h1>
|
||||
<p>
|
||||
@@ -48,15 +49,8 @@ export default function Contact() {
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{showSuccess && (
|
||||
<Alert className="border-green-500 bg-green-50 text-green-800">
|
||||
<CheckCircle2 className="h-5 w-5 text-green-600"/>
|
||||
<AlertTitle>Message envoyé !</AlertTitle>
|
||||
<AlertDescription>{flash.success}</AlertDescription>
|
||||
</Alert>
|
||||
|
||||
// Clean form
|
||||
|
||||
{showFlashMessage && (
|
||||
<FlashMessage messages={flash ?? {}} />
|
||||
)}
|
||||
|
||||
<Form
|
||||
@@ -68,7 +62,7 @@ export default function Contact() {
|
||||
<div className="lg:w-5xl px-10">
|
||||
<div className="flex gap-6 w-full">
|
||||
<div className="w-1/2">
|
||||
<div className="grid gap-2">
|
||||
<div className="grid gap-2 my-4">
|
||||
<Label htmlFor="lastname">Nom*</Label>
|
||||
<Input
|
||||
id="lastname"
|
||||
@@ -86,7 +80,7 @@ export default function Contact() {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-2">
|
||||
<div className="grid gap-2 my-4">
|
||||
<Label htmlFor="firstname">Prénom*</Label>
|
||||
<Input
|
||||
id="firstname"
|
||||
@@ -104,7 +98,7 @@ export default function Contact() {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-2">
|
||||
<div className="grid gap-2 my-4">
|
||||
<Label htmlFor="email">Adresse Mail*</Label>
|
||||
<Input
|
||||
id="email"
|
||||
@@ -118,7 +112,7 @@ export default function Contact() {
|
||||
<InputError message={errors.email}/>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-2">
|
||||
<div className="grid gap-2 my-4">
|
||||
<Label htmlFor="address">Votre adresse postale</Label>
|
||||
<Input
|
||||
id="address"
|
||||
@@ -138,7 +132,7 @@ export default function Contact() {
|
||||
</div>
|
||||
|
||||
<div className="w-1/2">
|
||||
<div className="grid gap-2">
|
||||
<div className="grid gap-2 my-4">
|
||||
<Label htmlFor="subject">Objet de votre demande*</Label>
|
||||
<Select name="subject" required>
|
||||
<SelectTrigger tabIndex={5}>
|
||||
@@ -156,9 +150,10 @@ export default function Contact() {
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-2">
|
||||
<div className="grid gap-2 my-4">
|
||||
<Label htmlFor="message">Votre message</Label>
|
||||
<Textarea
|
||||
className="h-28"
|
||||
id="message"
|
||||
name="message"
|
||||
tabIndex={6}
|
||||
@@ -167,7 +162,7 @@ export default function Contact() {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-2">
|
||||
<div className="grid gap-2 my-4">
|
||||
<Label htmlFor="captcha">Captcha</Label>
|
||||
<Input
|
||||
id="captcha"
|
||||
@@ -197,7 +192,7 @@ export default function Contact() {
|
||||
</div>
|
||||
)}
|
||||
</Form>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {Form, Head} from "@inertiajs/react";
|
||||
import {Form, Head, usePage} from "@inertiajs/react";
|
||||
import {cn} from "@/lib/utils";
|
||||
import {CheckIcon, LoaderCircle} from 'lucide-react';
|
||||
import {CheckCircle2, CheckIcon, LoaderCircle} from 'lucide-react';
|
||||
import MembershipFormController from "@/actions/App/Http/Controllers/Forms/MembershipFormController";
|
||||
import {Label} from "@/components/ui/label";
|
||||
import {Input} from "@/components/ui/input";
|
||||
@@ -8,38 +8,15 @@ import InputError from "@/components/input-error";
|
||||
import {Button} from "@/components/ui/button";
|
||||
import NavGuestLayout from "@/layouts/nav-guest-layout";
|
||||
import {Checkbox} from "@/components/ui/checkbox";
|
||||
import {useState} from "react";
|
||||
import {useEffect, useState} from "react";
|
||||
import {PageProps} from "@/types";
|
||||
import {FlashMessage} from "@/components/flash-message";
|
||||
|
||||
export default function Membership() {
|
||||
const today = new Date();
|
||||
const actualMonth = today.getMonth() + 1;
|
||||
const leftMonths = 12 - actualMonth;
|
||||
|
||||
|
||||
const [selectedPlan, setSelectedPlan] = useState<
|
||||
"custom" | "one-year" | "two-year"
|
||||
>("one-year");
|
||||
|
||||
const plans = [
|
||||
{
|
||||
id: "custom" as const,
|
||||
name: "Sur-mesure",
|
||||
price: `${leftMonths}€`,
|
||||
description: "Derniers mois de l'année.",
|
||||
},
|
||||
{
|
||||
id: "one-year" as const,
|
||||
name: "Un an",
|
||||
price: "12€",
|
||||
description: "Pour nous soutenir durant un an.",
|
||||
},
|
||||
{
|
||||
id: "two-year" as const,
|
||||
name: "Deux ans",
|
||||
price: "24€",
|
||||
description: "Pour nous soutenir durant deux ans.",
|
||||
},
|
||||
];
|
||||
const {flash, plans} = usePage().props as PageProps
|
||||
const [showFlashMessage, setFlashMessage] = useState(!!flash);
|
||||
const [selectedPlan, setSelectedPlan] = useState(plans?.[0]?.identifier ?? null);
|
||||
const [amount, setAmount] = useState(plans?.[0]?.price ?? 0);
|
||||
const features = [
|
||||
"Boîte Mail",
|
||||
"NextCloud",
|
||||
@@ -49,6 +26,42 @@ export default function Membership() {
|
||||
"Et plus encore ...",
|
||||
];
|
||||
|
||||
// /!\ Existant à discuter avec client
|
||||
/*const today = new Date();
|
||||
const actualMonth = today.getMonth() + 1;
|
||||
const leftMonths = 12 - actualMonth;*/
|
||||
/*const getAmount = (plan: string | null): number => {
|
||||
if (!plan) return 0;
|
||||
const baseAmount = leftMonths;
|
||||
switch (plan) {
|
||||
case 'custom':
|
||||
return baseAmount;
|
||||
case 'one-year':
|
||||
return baseAmount + 12;
|
||||
case 'two-year':
|
||||
return baseAmount + 24;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
};*/
|
||||
|
||||
useEffect(() => {
|
||||
if (plans && selectedPlan) {
|
||||
const plan = plans.find(p => p.identifier === selectedPlan);
|
||||
if (plan) {
|
||||
setAmount(plan.price);
|
||||
}
|
||||
}
|
||||
}, [selectedPlan, plans]);
|
||||
|
||||
useEffect(() => {
|
||||
if (flash) {
|
||||
setFlashMessage(true);
|
||||
const timer = setTimeout(() => setFlashMessage(false), 5000);
|
||||
return () => clearTimeout(timer);
|
||||
}
|
||||
}, [flash]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head title="Adhérer au Retzien Libre"/>
|
||||
@@ -56,13 +69,18 @@ export default function Membership() {
|
||||
className="flex min-h-screen flex-col items-center bg-[#F5F5F5] p-6 text-[#1b1b18] lg:justify-center lg:p-8 dark:bg-[#0a0a0a]">
|
||||
<NavGuestLayout/>
|
||||
|
||||
<div className="flex flex-col items-center justify-center gap-4">
|
||||
<section className="flex flex-col items-center justify-center gap-4">
|
||||
<div>
|
||||
<h1>Adhérer au Retzien Libre</h1>
|
||||
<p>
|
||||
Saisissez vos informations ci-dessous pour créer une demande d'adhésion :
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{showFlashMessage && (
|
||||
<FlashMessage messages={flash ?? {}}/>
|
||||
)}
|
||||
|
||||
<Form
|
||||
{...MembershipFormController.store.form()}
|
||||
resetOnSuccess
|
||||
@@ -70,9 +88,10 @@ export default function Membership() {
|
||||
className="flex flex-col gap-6"
|
||||
>
|
||||
{({processing, errors}) => (
|
||||
<>
|
||||
<div className="grid gap-6">
|
||||
<div className="grid gap-2">
|
||||
<div className="lg:w-5xl px-10">
|
||||
<div className="flex flex-col md:flex-row gap-6 w-full">
|
||||
<div className="w-full lg:w-1/2">
|
||||
<div className="grid gap-2 my-4">
|
||||
<Label htmlFor="lastname">Nom*</Label>
|
||||
<Input
|
||||
id="lastname"
|
||||
@@ -89,8 +108,7 @@ export default function Membership() {
|
||||
className="mt-2"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-2">
|
||||
<div className="grid gap-2 my-4">
|
||||
<Label htmlFor="firstname">Prénom*</Label>
|
||||
<Input
|
||||
id="firstname"
|
||||
@@ -107,43 +125,56 @@ export default function Membership() {
|
||||
className="mt-2"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-2">
|
||||
<div className="grid gap-2 my-4">
|
||||
<Label htmlFor="company">Société</Label>
|
||||
<Input
|
||||
id="company"
|
||||
type="text"
|
||||
autoFocus
|
||||
tabIndex={3}
|
||||
autoComplete="company"
|
||||
name="company"
|
||||
placeholder="Votre société"
|
||||
/>
|
||||
<InputError
|
||||
message={errors.firstname}
|
||||
className="mt-2"
|
||||
/>
|
||||
</div>
|
||||
<div className="grid gap-2 my-4">
|
||||
<Label htmlFor="email">Adresse Mail*</Label>
|
||||
<Input
|
||||
id="email"
|
||||
type="email"
|
||||
required
|
||||
tabIndex={3}
|
||||
tabIndex={4}
|
||||
autoComplete="email"
|
||||
name="email"
|
||||
placeholder="email@exemple.com"
|
||||
/>
|
||||
<InputError message={errors.email}/>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-2">
|
||||
<div className="grid gap-2 my-4">
|
||||
<Label htmlFor="phone1">Téléphone*</Label>
|
||||
<Input
|
||||
id="phone1"
|
||||
type="phone"
|
||||
required
|
||||
tabIndex={4}
|
||||
tabIndex={5}
|
||||
autoComplete="phone"
|
||||
name="phone1"
|
||||
placeholder="Votre numéro de téléphone"
|
||||
/>
|
||||
<InputError message={errors.phone}/>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-2">
|
||||
<div className="grid gap-2 my-4">
|
||||
<Label htmlFor="address">Votre adresse*</Label>
|
||||
<Input
|
||||
id="address"
|
||||
type="text"
|
||||
required
|
||||
autoFocus
|
||||
tabIndex={5}
|
||||
tabIndex={6}
|
||||
autoComplete="address"
|
||||
name="address"
|
||||
placeholder="Votre adresse"
|
||||
@@ -153,15 +184,14 @@ export default function Membership() {
|
||||
className="mt-2"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-2">
|
||||
<div className="grid gap-2 my-4">
|
||||
<Label htmlFor="zipcode">Votre code postale*</Label>
|
||||
<Input
|
||||
id="zipcode"
|
||||
type="text"
|
||||
required
|
||||
autoFocus
|
||||
tabIndex={6}
|
||||
tabIndex={7}
|
||||
autoComplete="zipcode"
|
||||
name="zipcode"
|
||||
placeholder="Votre code postale"
|
||||
@@ -171,15 +201,14 @@ export default function Membership() {
|
||||
className="mt-2"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-2">
|
||||
<div className="grid gap-2 my-4">
|
||||
<Label htmlFor="city">Votre ville*</Label>
|
||||
<Input
|
||||
id="city"
|
||||
type="text"
|
||||
required
|
||||
autoFocus
|
||||
tabIndex={6}
|
||||
tabIndex={8}
|
||||
autoComplete="city"
|
||||
name="city"
|
||||
placeholder="Votre ville"
|
||||
@@ -189,35 +218,46 @@ export default function Membership() {
|
||||
className="mt-2"
|
||||
/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div className="w-full lg:w-1/2">
|
||||
<div className="space-y-4">
|
||||
<Label htmlFor="package">Formule d'adhésion*</Label>
|
||||
<div className="grid grid-cols-3 gap-2">
|
||||
{plans.map((plan) => (
|
||||
<div className="grid grid-cols-3 gap-2 my-4">
|
||||
{plans?.map((plan) => (
|
||||
<button
|
||||
key={plan.id}
|
||||
type="button"
|
||||
tabIndex={8}
|
||||
onClick={() => setSelectedPlan(plan.identifier)}
|
||||
className={cn(
|
||||
"flex flex-col items-center justify-center rounded border-3 p-4 transition-colors",
|
||||
selectedPlan === plan.id
|
||||
selectedPlan === plan.identifier
|
||||
? "border-primary"
|
||||
: "border-black hover:border-primary"
|
||||
)}
|
||||
key={plan.id}
|
||||
onClick={() => setSelectedPlan(plan.id)}
|
||||
type="button"
|
||||
>
|
||||
<span className="font-semibold">{plan.name}</span>
|
||||
<span className="font-bold text-lg">{plan.price}</span>
|
||||
<span className="font-bold text-lg">{plan.price}€</span>
|
||||
<span className="text-center text-muted-foreground text-xs">
|
||||
{plan.description}
|
||||
</span>
|
||||
</button>
|
||||
))}
|
||||
<input type="hidden" name="package" value={selectedPlan ?? ''} />
|
||||
<input type="hidden" name="amount" value={amount} />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<div className="flex-col gap-6 ">
|
||||
<div className="text-center">
|
||||
<p className="text-center font-semibold text-lg">
|
||||
Montant à payer : <br/> <span className="text-primary">{amount} €</span>
|
||||
</p>
|
||||
</div>
|
||||
<div className="pl-10 space-y-2">
|
||||
<h4 className="font-semibold text-sm">Fonctionnalités inclues :</h4>
|
||||
<ul className="space-y-2">
|
||||
{features.map((feature, index) => (
|
||||
<li className="flex items-center gap-2 text-sm" key={index}>
|
||||
<li className="flex items-center gap-2 my-4 text-sm"
|
||||
key={index}>
|
||||
<CheckIcon className="h-4 w-4 text-primary"/>
|
||||
<span>{feature}</span>
|
||||
</li>
|
||||
@@ -226,14 +266,11 @@ export default function Membership() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/*<div className="grid gap-2">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Switch id="cloud-access" tabIndex={8}/>
|
||||
<Label htmlFor="cloud-access">Me créer un accès au service du "cloud" ?</Label>
|
||||
</div>
|
||||
</div>*/}
|
||||
|
||||
<div className="grid gap-2">
|
||||
</div>
|
||||
</div>
|
||||
<div className="mx-auto justify-center">
|
||||
<div className="w-[300px] grid gap-2 my-4">
|
||||
<Label htmlFor="captcha">Captcha</Label>
|
||||
<Input
|
||||
id="captcha"
|
||||
@@ -244,8 +281,7 @@ export default function Membership() {
|
||||
placeholder="Entrez le captcha ci-dessous"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-2">
|
||||
<div className="grid gap-2 my-4">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="cgu"
|
||||
@@ -253,16 +289,17 @@ export default function Membership() {
|
||||
tabIndex={10}
|
||||
required
|
||||
/>
|
||||
<Label htmlFor="remember">J'ai lu et j'accepte les <a href="#">C.G.U.</a>,
|
||||
<Label htmlFor="remember">J'ai lu et j'accepte les <a
|
||||
href="#">C.G.U.</a>,
|
||||
je comprends la nécessité des enregistrements de mes données
|
||||
personnelles.</Label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-center items-center">
|
||||
<Button
|
||||
variant="outline"
|
||||
type="submit"
|
||||
className="mt-2 w-full"
|
||||
className="mt-2 w-full max-w-1/3"
|
||||
tabIndex={11}
|
||||
data-test="register-user-button"
|
||||
>
|
||||
@@ -272,10 +309,11 @@ export default function Membership() {
|
||||
Envoyer
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Form>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
|
||||
@@ -448,7 +448,7 @@ contact.form = contactForm
|
||||
|
||||
/**
|
||||
* @see \App\Http\Controllers\Forms\MembershipFormController::membership
|
||||
* @see app/Http/Controllers/Forms/MembershipFormController.php:23
|
||||
* @see app/Http/Controllers/Forms/MembershipFormController.php:21
|
||||
* @route '/membership'
|
||||
*/
|
||||
export const membership = (options?: RouteQueryOptions): RouteDefinition<'get'> => ({
|
||||
@@ -463,7 +463,7 @@ membership.definition = {
|
||||
|
||||
/**
|
||||
* @see \App\Http\Controllers\Forms\MembershipFormController::membership
|
||||
* @see app/Http/Controllers/Forms/MembershipFormController.php:23
|
||||
* @see app/Http/Controllers/Forms/MembershipFormController.php:21
|
||||
* @route '/membership'
|
||||
*/
|
||||
membership.url = (options?: RouteQueryOptions) => {
|
||||
@@ -472,7 +472,7 @@ membership.url = (options?: RouteQueryOptions) => {
|
||||
|
||||
/**
|
||||
* @see \App\Http\Controllers\Forms\MembershipFormController::membership
|
||||
* @see app/Http/Controllers/Forms/MembershipFormController.php:23
|
||||
* @see app/Http/Controllers/Forms/MembershipFormController.php:21
|
||||
* @route '/membership'
|
||||
*/
|
||||
membership.get = (options?: RouteQueryOptions): RouteDefinition<'get'> => ({
|
||||
@@ -482,7 +482,7 @@ membership.get = (options?: RouteQueryOptions): RouteDefinition<'get'> => ({
|
||||
|
||||
/**
|
||||
* @see \App\Http\Controllers\Forms\MembershipFormController::membership
|
||||
* @see app/Http/Controllers/Forms/MembershipFormController.php:23
|
||||
* @see app/Http/Controllers/Forms/MembershipFormController.php:21
|
||||
* @route '/membership'
|
||||
*/
|
||||
membership.head = (options?: RouteQueryOptions): RouteDefinition<'head'> => ({
|
||||
@@ -492,7 +492,7 @@ membership.head = (options?: RouteQueryOptions): RouteDefinition<'head'> => ({
|
||||
|
||||
/**
|
||||
* @see \App\Http\Controllers\Forms\MembershipFormController::membership
|
||||
* @see app/Http/Controllers/Forms/MembershipFormController.php:23
|
||||
* @see app/Http/Controllers/Forms/MembershipFormController.php:21
|
||||
* @route '/membership'
|
||||
*/
|
||||
const membershipForm = (options?: RouteQueryOptions): RouteFormDefinition<'get'> => ({
|
||||
@@ -502,7 +502,7 @@ const membershipForm = (options?: RouteQueryOptions): RouteFormDefinition<'get'>
|
||||
|
||||
/**
|
||||
* @see \App\Http\Controllers\Forms\MembershipFormController::membership
|
||||
* @see app/Http/Controllers/Forms/MembershipFormController.php:23
|
||||
* @see app/Http/Controllers/Forms/MembershipFormController.php:21
|
||||
* @route '/membership'
|
||||
*/
|
||||
membershipForm.get = (options?: RouteQueryOptions): RouteFormDefinition<'get'> => ({
|
||||
@@ -512,7 +512,7 @@ membershipForm.get = (options?: RouteQueryOptions): RouteFormDefinition<'get'> =
|
||||
|
||||
/**
|
||||
* @see \App\Http\Controllers\Forms\MembershipFormController::membership
|
||||
* @see app/Http/Controllers/Forms/MembershipFormController.php:23
|
||||
* @see app/Http/Controllers/Forms/MembershipFormController.php:21
|
||||
* @route '/membership'
|
||||
*/
|
||||
membershipForm.head = (options?: RouteQueryOptions): RouteFormDefinition<'get'> => ({
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { queryParams, type RouteQueryOptions, type RouteDefinition, type RouteFormDefinition } from './../../wayfinder'
|
||||
/**
|
||||
* @see \App\Http\Controllers\Forms\MembershipFormController::store
|
||||
* @see app/Http/Controllers/Forms/MembershipFormController.php:37
|
||||
* @see app/Http/Controllers/Forms/MembershipFormController.php:35
|
||||
* @route '/membership'
|
||||
*/
|
||||
export const store = (options?: RouteQueryOptions): RouteDefinition<'post'> => ({
|
||||
@@ -16,7 +16,7 @@ store.definition = {
|
||||
|
||||
/**
|
||||
* @see \App\Http\Controllers\Forms\MembershipFormController::store
|
||||
* @see app/Http/Controllers/Forms/MembershipFormController.php:37
|
||||
* @see app/Http/Controllers/Forms/MembershipFormController.php:35
|
||||
* @route '/membership'
|
||||
*/
|
||||
store.url = (options?: RouteQueryOptions) => {
|
||||
@@ -25,7 +25,7 @@ store.url = (options?: RouteQueryOptions) => {
|
||||
|
||||
/**
|
||||
* @see \App\Http\Controllers\Forms\MembershipFormController::store
|
||||
* @see app/Http/Controllers/Forms/MembershipFormController.php:37
|
||||
* @see app/Http/Controllers/Forms/MembershipFormController.php:35
|
||||
* @route '/membership'
|
||||
*/
|
||||
store.post = (options?: RouteQueryOptions): RouteDefinition<'post'> => ({
|
||||
@@ -35,7 +35,7 @@ store.post = (options?: RouteQueryOptions): RouteDefinition<'post'> => ({
|
||||
|
||||
/**
|
||||
* @see \App\Http\Controllers\Forms\MembershipFormController::store
|
||||
* @see app/Http/Controllers/Forms/MembershipFormController.php:37
|
||||
* @see app/Http/Controllers/Forms/MembershipFormController.php:35
|
||||
* @route '/membership'
|
||||
*/
|
||||
const storeForm = (options?: RouteQueryOptions): RouteFormDefinition<'post'> => ({
|
||||
@@ -45,7 +45,7 @@ const storeForm = (options?: RouteQueryOptions): RouteFormDefinition<'post'> =>
|
||||
|
||||
/**
|
||||
* @see \App\Http\Controllers\Forms\MembershipFormController::store
|
||||
* @see app/Http/Controllers/Forms/MembershipFormController.php:37
|
||||
* @see app/Http/Controllers/Forms/MembershipFormController.php:35
|
||||
* @route '/membership'
|
||||
*/
|
||||
storeForm.post = (options?: RouteQueryOptions): RouteFormDefinition<'post'> => ({
|
||||
|
||||
26
resources/js/types/index.d.ts
vendored
26
resources/js/types/index.d.ts
vendored
@@ -41,3 +41,29 @@ export interface User {
|
||||
updated_at: string;
|
||||
[key: string]: unknown; // This allows for additional properties...
|
||||
}
|
||||
|
||||
export interface FlashMessages {
|
||||
success?: string;
|
||||
error?: string;
|
||||
warning?: string;
|
||||
info?: string;
|
||||
}
|
||||
|
||||
export interface Plans {
|
||||
id: number;
|
||||
identifier: string;
|
||||
name: string;
|
||||
price: number;
|
||||
description?: string,
|
||||
is_active: boolean;
|
||||
}
|
||||
|
||||
|
||||
export interface PageProps {
|
||||
flash?: FlashMessages;
|
||||
auth?: Auth;
|
||||
plans?: Plans[];
|
||||
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user