Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
98 commits
Select commit Hold shift + click to select a range
151f174
add: laravel configuration
monte-dev Feb 21, 2026
3a7365c
add: core MVp migrations (users, profiles, venues, events, ticketing,…
monte-dev Feb 21, 2026
2c31134
refactor: restructure app into Domain-Driven Design modules
monte-dev Feb 21, 2026
f207ba2
feat(auth): implement domain-driven auth api with services, invokable…
monte-dev Feb 21, 2026
46c2bf8
docs(bruno): add bruno api collection for auth testing
monte-dev Feb 21, 2026
d1d8816
Merge pull request #1 from monte-dev/feature/1-auth-api
monte-dev Feb 21, 2026
a0ff406
Merge pull request #2 from monte-dev/feature/1-architecture-refactor-ddd
monte-dev Feb 21, 2026
7f0f56c
Merge pull request #3 from monte-dev/feature/1-core-migrations
monte-dev Feb 21, 2026
cf77679
Merge pull request #4 from monte-dev/feature/1-laravel-configuration
monte-dev Feb 21, 2026
be52a2b
feat(admin): add organizer approval endpoint and notification email
monte-dev Feb 22, 2026
37bd5d0
feat(organizer): add organizer profile creation api
monte-dev Feb 22, 2026
6a1326c
test(organizer): organizer onboarding and approval feature test
monte-dev Feb 22, 2026
fdd86dd
feat(venues): add venues index, and typeahead search endpoints
monte-dev Feb 22, 2026
f2df94b
test(venues): add venue search feature tests
monte-dev Feb 22, 2026
00bfda1
feat(events): add organizer Crud with eventPolicy, requests, resource…
monte-dev Feb 22, 2026
fc50883
feat(events): auto-slug generation on event create, service /reposito…
monte-dev Feb 22, 2026
c217cb2
test(events): feature tests
monte-dev Feb 22, 2026
90d7b96
feat(events): add EventCancelledNotification draft for p4
monte-dev Feb 22, 2026
c828ead
feat(categories): add categories index with event counts
monte-dev Feb 22, 2026
eba71f1
feat(events): add public events listing with filters
monte-dev Feb 22, 2026
4982c8f
feat(events): add Meilisearhch full-text search on events
monte-dev Feb 22, 2026
c9bb252
test(events): add feature tests
monte-dev Feb 22, 2026
3e5aadf
feat(venues): veneu submission migration, models, routes
monte-dev Feb 22, 2026
a84acfb
feat(venues): venue status consts, controller , http enum
monte-dev Feb 22, 2026
bfde623
feat(venues): venue submission service and repository logicc, Job ske…
monte-dev Feb 22, 2026
461f4e6
test(venues): venue submission feature testts
monte-dev Feb 22, 2026
62f0575
feat(venues): integrate Google Places API
monte-dev Feb 23, 2026
dad3e04
feat(venues): GooglePlaces api unit and integration tests
monte-dev Feb 23, 2026
641934b
refactor: add bruno endpoints
monte-dev Feb 23, 2026
103c5bc
refactor: fix auth endpoints
monte-dev Feb 23, 2026
32f3e84
feat(venues): add Embedding Service for Openai integration
monte-dev Feb 23, 2026
51ce1df
feat(venues): add ProcessVenueSubmissionJob and logic
monte-dev Feb 23, 2026
2aad915
feat(venues): add Vanue similarity checker
monte-dev Feb 23, 2026
b9caa5e
test(venues): venue similarity checker tests
monte-dev Feb 23, 2026
d8ff580
feat(admin): admin venu esubmission approve/reject/merge/ endpoints
monte-dev Feb 23, 2026
1ad71c4
feat(admin): admin submissions logic and notifications
monte-dev Feb 23, 2026
50145a4
test(admin): venue submission admin review feature tests
monte-dev Feb 23, 2026
9732dcb
test(admin): venue submission additional tests
monte-dev Feb 23, 2026
fa43467
test(venues): additional error paths testS
monte-dev Feb 23, 2026
3b2a544
test(organizer): organizer and events feature tests enhancements
monte-dev Feb 23, 2026
e60577a
test(auth): add negative tests and password reset tests, refactor bas…
monte-dev Feb 23, 2026
5cb0802
test(categories): add CategoryApiTest to complete domain coverage
monte-dev Feb 23, 2026
352bc72
feat(tickets): add registerAttendeeAction with atomic DB transaction …
monte-dev Feb 23, 2026
1b3b101
feat(tickets): add ticketPolicy and model relations
monte-dev Feb 23, 2026
893591d
feat(tickets): add ticket retrieval and registration logic
monte-dev Feb 23, 2026
28ba385
feat(tickets): add enums, exceptions and resources for Ticketing domain
monte-dev Feb 23, 2026
aa2931c
test(tickets): ticket registration and cancellation tests
monte-dev Feb 23, 2026
711a732
feat(tickets): add Ticket mailing job and qr code generation using B…
monte-dev Feb 23, 2026
47bd9d1
feat(tickets): add ticket mails
monte-dev Feb 23, 2026
d908406
add email resending and test command
monte-dev Feb 23, 2026
c9d073b
test(tickets): add ticket mailing tests
monte-dev Feb 23, 2026
67e73b8
feat(organizer): attendee list endpoint with pagination and service l…
monte-dev Feb 24, 2026
c6baa6b
feat(organizer): CSV export queued job with signed S3 download URL
monte-dev Feb 24, 2026
e95c614
feat(organizer): QR check-in endpoint with event ownership validation
monte-dev Feb 24, 2026
ffa1325
test(organizer): attendee management feature tests (list, export, che…
monte-dev Feb 24, 2026
7958b69
refactor(errors): standardize domain error handling across controller…
monte-dev Feb 24, 2026
73be8a7
chore(api): add missing bruno ednpoints
monte-dev Feb 24, 2026
7023bae
Merge pull request #6 from monte-dev/feature/5-attendee-managemenet-api
monte-dev Feb 24, 2026
8699ccf
Merge pull request #10 from monte-dev/feature/4-ticketing-system
monte-dev Feb 24, 2026
e87b56b
Merge pull request #8 from monte-dev/feature/4-qr-codes-and-ticket-em…
monte-dev Feb 24, 2026
71baec5
Merge pull request #11 from monte-dev/feature/3-admin-venue-review-api
monte-dev Feb 24, 2026
5b9695a
Merge pull request #12 from monte-dev/feature/3-venue-submission
monte-dev Feb 24, 2026
4803fff
Merge pull request #13 from monte-dev/feature/2-event-crud
monte-dev Feb 24, 2026
0f4f05d
Merge pull request #14 from monte-dev/feature/2-public-events-api
monte-dev Feb 24, 2026
9dfe09e
Merge pull request #15 from monte-dev/feature/2-venue-api
monte-dev Feb 24, 2026
7cf2b9d
feat(events): update events schema and create event_instances table, …
monte-dev Feb 25, 2026
179e3ae
feat(events): implement SyncEventInstancesAction and RecurrenceFreque…
monte-dev Feb 25, 2026
9aeddf5
feat(update RegisterAttendeeAction and resources for new events insta…
monte-dev Feb 25, 2026
b6942bb
feat(events): refactor PublicEventController to support instance-base…
monte-dev Feb 25, 2026
604efa7
test(events): add tests for recurrence logic and instances
monte-dev Feb 25, 2026
272a57e
test(events): update existing tests to include events_instance logic
monte-dev Feb 25, 2026
e3c1bb2
Merge pull request #16 from monte-dev/feature/6-event-instances
monte-dev Feb 25, 2026
80a53b8
feat(dashboard): add summary statistics for organizer dashboard
monte-dev Feb 25, 2026
2267623
feat(dashboard): implement recent registration activity feed
monte-dev Feb 25, 2026
0d44a56
feat(analytics): add time-series data for registration trends
monte-dev Feb 25, 2026
79bf7be
test(analytics): verify dashboard and analytics data integrity
monte-dev Feb 25, 2026
a02daed
feat(analytics): add bruno endpoints , refactor events code
monte-dev Feb 25, 2026
b28f97e
feat(admin): admin moderation logic
monte-dev Feb 25, 2026
b2e073c
feat: update repositories for admin and organizer stats methods
monte-dev Feb 25, 2026
2e77b41
add rate limiting
monte-dev Feb 25, 2026
6aa136b
refactor: decouple public event logic into EventInstance service and …
monte-dev Feb 25, 2026
37a0ef1
test(admin): add AdminGeneralTEst
monte-dev Feb 25, 2026
53216f5
Merge pull request #17 from monte-dev/feature/6.3-admin-moderation-pr…
monte-dev Feb 25, 2026
e81b975
Merge pull request #19 from monte-dev/feature/6.2-organizer-dashboard
monte-dev Feb 25, 2026
7a06cc0
feat(events): add specific instance cancellation and expose remaining…
monte-dev Feb 25, 2026
2523858
feat: add organizer logo upload and update related resources
monte-dev Feb 25, 2026
84e9d8c
Merge pull request #20 from monte-dev/feature/6.4-organizer-logo-canc…
monte-dev Feb 25, 2026
b565eda
feat(payments): add stripe sdk and base domain model updates
monte-dev Feb 26, 2026
034f4c3
feat(payments): implement database schema and api routing for payments
monte-dev Feb 26, 2026
9140a15
feat(payments): implement stripe provider with escrow payout model
monte-dev Feb 26, 2026
4eb37bd
chore(bruno): remove obsolete ticket store request
monte-dev Feb 26, 2026
cb88bff
chore(bruno): rename all bruno endpoints
monte-dev Feb 26, 2026
172c9d3
docs(payments): update readme and add testing tools for stripe flow
monte-dev Feb 26, 2026
635a3c6
docs(payments): update readme and add testing tools for stripe flow
monte-dev Feb 26, 2026
ccbb750
docs: add README.md
monte-dev Feb 28, 2026
580b9a5
Merge pull request #21 from monte-dev/feature/7-readme-project-presen…
monte-dev Feb 28, 2026
14b907a
docs: add openAI and google places integration details to README
monte-dev Feb 28, 2026
0dad37d
Update README.md
monte-dev Jul 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 34 additions & 4 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -47,19 +47,49 @@ REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379

MAIL_MAILER=log
MAIL_MAILER=smtp
MAIL_SCHEME=null
MAIL_HOST=127.0.0.1
MAIL_PORT=2525

# Use [::1] on Windows/WSL if 127.0.0.1 fails for Mailpit
MAIL_HOST=[::1]
MAIL_PORT=1025
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_FROM_ADDRESS="hello@example.com"
MAIL_FROM_ADDRESS="hello@example.pl"
MAIL_FROM_NAME="${APP_NAME}"

MEILISEARCH_HOST=http://127.0.0.1:7700
MEILISEARCH_KEY=masterKey

FRONTEND_URL=http://localhost:3000
FRONTEND_URL_LOCAL_STRIPE_DUMMY=https://github.com
# Ticketing & QR Codes
BACON_QR_RENDERER=gd

AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false

VITE_APP_NAME="${APP_NAME}"


API_VERSION=v1

SCOUT_DRIVER=meilisearch
MEILISEARCH_HOST=http://127.0.0.1:7700
MEILISEARCH_KEY=

STRIPE_TEST_PUBLISHABLE_KEY=
# STRIPE_PUBLISHABLE_KEY=
# STRIPE_SECRET_KEY=
STRIPE_TEST_SECRET_KEY=

STRIPE_WEBHOOK_SECRET=

GOOGLE_PLACES_API_KEY=
GOOGLE_PLACES_API_URL=https://places.googleapis.com/v1/places:searchText

OPEN_AI_API_KEY=
OPENAI_EMBEDDINGS_URL=https://api.openai.com/v1/embeddings
390 changes: 351 additions & 39 deletions README.md

Large diffs are not rendered by default.

66 changes: 66 additions & 0 deletions app/Console/Commands/ConfirmOrderCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php

namespace App\Console\Commands;

use App\Domains\Ticketing\Models\Order;
use App\Domains\Payments\Enums\PaymentStatusEnum;
use App\Domains\Ticketing\Enums\TicketStatusEnum;
use App\Domains\Payments\Models\Payout;
use App\Domains\Payments\Enums\PayoutStatusEnum;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;

class ConfirmOrderCommand extends Command
{
protected $signature = 'app:confirm-order {id}';
protected $description = 'Manually confirm an order payment (Simulates a success webhook)';

public function handle(): void
{
if (app()->isProduction()) {
$this->error('This command is for local development and testing only. Do not run in prod');
return;
}

$orderId = $this->argument('id');
$order = Order::with('tickets', 'eventInstance.event')->find($orderId);

if (!$order) {
$this->error("Order #{$orderId} not found.");
return;
}

if ($order->payment_status === PaymentStatusEnum::PAID) {
$this->warn("Order #{$orderId} is already paid.");
return;
}

$this->info("Confirming Order #{$orderId}...");

DB::transaction(function () use ($order) {
$order->markAsPaid(
intentId: 'manual_' . uniqid(),
provider: 'stripe'
);

foreach ($order->tickets as $ticket) {
$ticket->update(['status' => TicketStatusEnum::VALID]);
}

$feePercentage = config('payments.platform_fee_percentage', 5);
$feeAmount = (int) round($order->total_amount * ($feePercentage / 100));
$netAmount = $order->total_amount - $feeAmount;

Payout::create([
'organizer_profile_id' => $order->eventInstance->event->organizer_profile_id,
'order_id' => $order->id,
'amount' => $netAmount,
'currency' => $order->currency,
'status' => PayoutStatusEnum::PENDING,
'scheduled_for' => $order->eventInstance->ends_at->addHours(24),
]);
});

$this->info("Success! Order confirmed and Payout scheduled for release.");
}
}
34 changes: 34 additions & 0 deletions app/Console/Commands/TestTicketEmailCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

namespace App\Console\Commands;

use App\Domains\Ticketing\Jobs\SendTicketEmailJob;
use App\Domains\Ticketing\Models\Ticket;
use Illuminate\Console\Command;

class TestTicketEmailCommand extends Command
{
protected $signature = 'mail:test-ticket {ticket_id?}';
protected $description = 'Send a test ticket email to verify MJML layout in Mailpit';

public function handle()
{
$ticketId = $this->argument('ticket_id');

$ticket = $ticketId
? Ticket::findOrFail($ticketId)
: Ticket::latest()->first();

if (!$ticket) {
$this->error('No tickets found in database. Please register a ticket first.');
return 1;
}

$this->info("Dispatching email for Ticket ID: {$ticket->id} (UUID: {$ticket->uuid}) to {$ticket->email}...");

SendTicketEmailJob::dispatch($ticket);

$this->info('Job dispatched. Make sure queue worker is running');
return 0;
}
}
25 changes: 25 additions & 0 deletions app/Domains/Admin/Controllers/AdminStatsController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

namespace App\Domains\Admin\Controllers;

use App\Domains\Admin\Enums\Http\AdminHttpEnum;
use App\Domains\Admin\Services\AdminStatsService;
use App\Domains\Shared\Http\BaseController;
use Illuminate\Http\JsonResponse;
use Symfony\Component\HttpFoundation\Response;

class AdminStatsController extends BaseController
{
public function __construct(
protected AdminStatsService $adminStatsService
) {}

public function __invoke(): JsonResponse
{
return $this->apiResponseSuccess(
AdminHttpEnum::STATS_RETRIEVED->value,
Response::HTTP_OK,
['stats' => $this->adminStatsService->getGlobalStats()]
);
}
}
35 changes: 35 additions & 0 deletions app/Domains/Admin/Controllers/ApproveOrganizerController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

namespace App\Domains\Admin\Controllers;

use App\Domains\Auth\Models\User;
use App\Domains\Admin\Enums\Http\AdminHttpEnum;
use App\Domains\Organizer\Services\OrganizerService;
use App\Domains\Shared\Exceptions\DomainException;
use App\Domains\Shared\Http\BaseController;
use Exception;
use Illuminate\Http\JsonResponse;
use Symfony\Component\HttpFoundation\Response;

class ApproveOrganizerController extends BaseController
{
public function __construct(
protected OrganizerService $organizerService
) {}

public function __invoke(User $user): JsonResponse
{
try {
$this->organizerService->approveOrganizer($user);
} catch (DomainException $e) {
return $this->apiResponseError($e, null, Response::HTTP_BAD_REQUEST);
} catch (Exception $e) {
return $this->apiResponseError($e);
}

return $this->apiResponseSuccess(
AdminHttpEnum::ORGANIZER_APPROVED->value,
Response::HTTP_OK
);
}
}
35 changes: 35 additions & 0 deletions app/Domains/Admin/Controllers/RejectOrganizerController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

namespace App\Domains\Admin\Controllers;

use App\Domains\Admin\Enums\Http\AdminHttpEnum;
use App\Domains\Auth\Models\User;
use App\Domains\Organizer\Services\OrganizerService;
use App\Domains\Shared\Exceptions\DomainException;
use App\Domains\Shared\Http\BaseController;
use Exception;
use Illuminate\Http\JsonResponse;
use Symfony\Component\HttpFoundation\Response;

class RejectOrganizerController extends BaseController
{
public function __construct(
protected OrganizerService $organizerService
) {}

public function __invoke(User $user): JsonResponse
{
try {
$this->organizerService->rejectOrganizer($user);
} catch (DomainException $e) {
return $this->apiResponseError($e, null, Response::HTTP_BAD_REQUEST);
} catch (Exception $e) {
return $this->apiResponseError($e);
}

return $this->apiResponseSuccess(
AdminHttpEnum::ORGANIZER_REJECTED->value,
Response::HTTP_OK
);
}
}
10 changes: 10 additions & 0 deletions app/Domains/Admin/Enums/Http/AdminHttpEnum.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace App\Domains\Admin\Enums\Http;

enum AdminHttpEnum: string
{
case STATS_RETRIEVED = 'admin.stats.retrieved';
case ORGANIZER_APPROVED = 'admin.organizer.approved';
case ORGANIZER_REJECTED = 'admin.organizer.rejected';
}
26 changes: 26 additions & 0 deletions app/Domains/Admin/Services/AdminStatsService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

namespace App\Domains\Admin\Services;

use App\Domains\Events\Repositories\EventRepositoryInterface;
use App\Domains\Organizer\Repositories\OrganizerRepository;
use App\Domains\Ticketing\Repositories\TicketRepositoryInterface;

class AdminStatsService
{
public function __construct(
protected EventRepositoryInterface $eventRepository,
protected OrganizerRepository $organizerRepository,
protected TicketRepositoryInterface $ticketRepository
) {}

public function getGlobalStats(): array
{
return [
'total_events' => $this->eventRepository->getTotalCount(),
'total_organizers' => $this->organizerRepository->getTotalCount(),
'total_tickets_sold' => $this->ticketRepository->getTotalCount(),
'total_revenue' => $this->ticketRepository->getTotalRevenue(),
];
}
}
20 changes: 20 additions & 0 deletions app/Domains/Analytics/Models/AnalyticsEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace App\Domains\Analytics\Models;

use Illuminate\Database\Eloquent\Model;

class AnalyticsEvent extends Model
{
protected $fillable = [
'event_type',
'properties',
'user_id',
'session_id',
'created_at',
];

protected $casts = [
'properties' => 'array',
];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace App\Domains\Analytics\Repositories;

use Carbon\Carbon;

interface AnalyticsRepositoryInterface
{
public function getViewTimeSeries(int $organizerProfileId, ?int $eventId, Carbon $startDate): array;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

namespace App\Domains\Analytics\Repositories\Eloquent;

use App\Domains\Analytics\Models\AnalyticsEvent;
use App\Domains\Analytics\Repositories\AnalyticsRepositoryInterface;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;

class AnalyticsRepository implements AnalyticsRepositoryInterface
{
public function getViewTimeSeries(int $organizerProfileId, ?int $eventId, Carbon $startDate): array
{
$query = AnalyticsEvent::query()
->where('event_type', 'event.view')
->where('created_at', '>=', $startDate);

if ($eventId) {
$query->whereJsonContains('properties->event_id', (int) $eventId);
} else {
$eventIds = DB::table('events')
->where('organizer_profile_id', $organizerProfileId)
->pluck('id')
->toArray();

$query->where(function ($q) use ($eventIds) {
foreach ($eventIds as $id) {
$q->orWhereJsonContains('properties->event_id', (int) $id);
}
});
}

return $query->select([
DB::raw('DATE(created_at) as date'),
DB::raw('COUNT(*) as count')
])
->groupBy('date')
->orderBy('date')
->get()
->pluck('count', 'date')
->toArray();
}
}
Loading