diff --git a/app/Event.php b/app/Event.php
index 5d419ec5c..5d42b3c4a 100644
--- a/app/Event.php
+++ b/app/Event.php
@@ -79,7 +79,9 @@ class Event extends Model
'is_extracurricular_event',
'is_standard_school_curriculum',
'is_use_resource',
- 'ages'
+ 'ages',
+ 'source_ref',
+ 'source_synced_at'
];
public const ACTIVITY_FORMATS = [
@@ -175,6 +177,14 @@ protected function casts(): array
'is_standard_school_curriculum' => 'boolean',
'ages' => 'array',
'is_use_resource' => 'boolean',
+
+ 'start_date' => 'datetime',
+ 'end_date' => 'datetime',
+ 'pub_date' => 'datetime',
+ 'reported_at' => 'datetime',
+ 'certificate_generated_at' => 'datetime',
+ 'last_report_notification_sent_at' => 'datetime',
+ 'source_synced_at' => 'datetime',
];
}
diff --git a/app/Nova/Audience.php b/app/Nova/Audience.php
new file mode 100644
index 000000000..2f0fe4d1f
--- /dev/null
+++ b/app/Nova/Audience.php
@@ -0,0 +1,23 @@
+sortable(),
+ Text::make('Name')->sortable()->rules('required', 'max:255'),
+ ];
+ }
+}
diff --git a/app/Nova/Event.php b/app/Nova/Event.php
index bd86ef329..392eb0675 100644
--- a/app/Nova/Event.php
+++ b/app/Nova/Event.php
@@ -3,10 +3,20 @@
namespace App\Nova;
use Illuminate\Http\Request;
+use Laravel\Nova\Fields\Boolean;
+use Laravel\Nova\Fields\BelongsTo;
+use Laravel\Nova\Fields\BelongsToMany;
use Laravel\Nova\Fields\Country;
+use Laravel\Nova\Fields\DateTime;
+use Laravel\Nova\Fields\ID;
+use Laravel\Nova\Fields\MultiSelect;
+use Laravel\Nova\Fields\Number;
use Laravel\Nova\Fields\Select;
use Laravel\Nova\Fields\Text;
+use Laravel\Nova\Fields\URL;
use Laravel\Nova\Http\Requests\NovaRequest;
+use Illuminate\Support\Arr;
+use Laravel\Nova\Fields\Trix;
class Event extends Resource
{
@@ -31,7 +41,7 @@ class Event extends Resource
* @var array
*/
public static $search = [
- 'id', 'title',
+ 'id', 'title', 'geoposition'
];
/**
@@ -39,36 +49,186 @@ class Event extends Resource
*/
public function fields(Request $request): array
{
- return [
+ try {
+ return [
+ ID::make()->sortable(),
- Text::make('Title')->sortable(),
- Text::make('Certificate', function () {
- $certificate_url = $this->certificate_url;
+ // Core
+ Text::make('Title')->sortable()->rules('required', 'max:255'),
+ Text::make('Slug')->hideFromIndex()->rules('nullable', 'max:255'),
- return "{$certificate_url}";
- })->asHtml()->onlyOnDetail(),
- Text::make('Web Link', function () {
- $slug = $this->slug;
- $id = $this->id;
- $url = config('codeweek.app_url');
+ Country::make('Country', 'country_iso')->sortable()->rules('required', 'size:2'),
- return "View Activity's Page";
- })->asHtml()->onlyOnDetail(),
- Text::make('Description')->onlyOnDetail(),
- Text::make('Organizer')->onlyOnDetail(),
- Text::make('codeweek_for_all_participation_code')->sortable()->onlyOnDetail(),
+ Select::make('Status')
+ ->options([
+ 'APPROVED' => 'Approved',
+ 'REJECTED' => 'Rejected',
+ 'PENDING' => 'Pending',
+ ])->displayUsingLabels()->sortable()->rules('required'),
- Country::make('Country', 'country_iso')->sortable(),
+ Text::make('Organizer')->hideFromIndex()->rules('nullable', 'max:255'),
+ Text::make('Location')->hideFromIndex()->rules('nullable', 'max:255'),
- Select::make('Status')->options([
- 'APPROVED' => 'Approved',
- 'REJECTED' => 'Rejected',
- 'PENDING' => 'Pending',
- ]),
+ // Geo
+ Text::make('Geoposition')->hideFromIndex()->rules('nullable', 'max:255'),
+ Number::make('Latitude')->step('any')->hideFromIndex()->rules('nullable', 'numeric'),
+ Number::make('Longitude')->step('any')->hideFromIndex()->rules('nullable', 'numeric'),
- Text::make('Creator', 'owner.email'),
+ // Dates
+ DateTime::make('Start Date', 'start_date')->sortable()->rules('nullable', 'date'),
+ DateTime::make('End Date', 'end_date')->sortable()->rules('nullable', 'date'),
+ DateTime::make('Publication Date', 'pub_date')->onlyOnDetail()->rules('nullable', 'date'),
+ DateTime::make('Reported At', 'reported_at')->onlyOnDetail()->rules('nullable', 'date'),
+ DateTime::make('Certificate Generated At', 'certificate_generated_at')->onlyOnDetail()->rules('nullable', 'date'),
+ DateTime::make('Last Report Notification Sent At', 'last_report_notification_sent_at')->onlyOnDetail()->rules('nullable', 'date'),
+ DateTime::make('Source Synced At', 'source_synced_at')->onlyOnDetail()->rules('nullable', 'date'),
- ];
+ // Contacts & Links
+ URL::make('Event URL', 'event_url')->displayUsing(fn ($v) => $v)->hideFromIndex()->rules('nullable', 'url'),
+ Text::make('Contact Person')->hideFromIndex()->rules('nullable', 'max:255'),
+ Text::make('User Email', 'user_email')->hideFromIndex()->rules('nullable', 'email', 'max:255'),
+
+ // Media
+ URL::make('Picture')->displayUsing(fn ($v) => $v)->hideFromIndex()->rules('nullable', 'url'),
+ URL::make('Picture Detail', 'picture_detail')->displayUsing(fn ($v) => $v)->hideFromIndex()->rules('nullable', 'url'),
+ URL::make('Certificate URL', 'certificate_url')->displayUsing(fn ($v) => $v)->onlyOnDetail()->rules('nullable', 'url'),
+
+ // Descriptions
+ Trix::make('Description')->hideFromIndex(),
+ Text::make('Web Link', function () {
+ $slug = $this->slug;
+ $id = $this->id;
+ $url = config('codeweek.app_url');
+
+ return "View Activity's Page";
+ })->asHtml()->onlyOnDetail(),
+ Text::make('Certificate', function () {
+ $certificate_url = $this->certificate_url;
+ return $certificate_url
+ ? "{$certificate_url}"
+ : '-';
+ })->asHtml()->onlyOnDetail(),
+
+ // Classification
+ Text::make('Activity Type', 'activity_type')->hideFromIndex()->rules('nullable', 'max:255'),
+ Text::make('Organizer Type', 'organizer_type')->hideFromIndex()->rules('nullable', 'max:255'),
+
+ MultiSelect::make('Activity Format', 'activity_format')
+ ->options(array_combine(\App\Event::ACTIVITY_FORMATS, \App\Event::ACTIVITY_FORMATS))
+ ->nullable()
+ ->rules(function () {
+ return [
+ function ($attribute, $value, $fail) {
+ if ($value === null || $value === '' || $value === [] || $value === '[]') {
+ return;
+ }
+ if (!is_array($value)) {
+ $decoded = json_decode((string) $value, true);
+ if (json_last_error() !== JSON_ERROR_NONE || !is_array($decoded)) {
+ $fail(__('validation.array', ['attribute' => $attribute]));
+ }
+ }
+ },
+ ];
+ })
+ ->fillUsing(function ($request, $model, $attribute, $requestAttribute) {
+ try {
+ $raw = $request->input($requestAttribute);
+ $decoded = is_array($raw) ? $raw : (is_string($raw) ? json_decode($raw, true) : null);
+ $model->{$attribute} = is_array($decoded) ? array_values($decoded) : [];
+ } catch (\Throwable $e) {
+ $model->{$attribute} = [];
+ }
+ })
+ ->hideFromIndex(),
+
+ Select::make('Duration')->options(
+ array_combine(\App\Event::DURATIONS, \App\Event::DURATIONS)
+ )->displayUsingLabels()->hideFromIndex()->rules('nullable', 'in:'.implode(',', \App\Event::DURATIONS)),
+
+ Select::make('Recurring Type', 'recurring_type')->options(
+ array_combine(\App\Event::RECURRING_TYPES, \App\Event::RECURRING_TYPES)
+ )->displayUsingLabels()->hideFromIndex()->rules('nullable', 'in:'.implode(',', \App\Event::RECURRING_TYPES)),
+
+ Select::make('Recurring Event', 'recurring_event')->options(
+ array_combine(\App\Event::RECURRING_EVENTS, \App\Event::RECURRING_EVENTS)
+ )->displayUsingLabels()->hideFromIndex()->rules('nullable', 'in:'.implode(',', \App\Event::RECURRING_EVENTS)),
+
+ MultiSelect::make('Ages', 'ages')
+ ->options(array_combine(\App\Event::AGES, \App\Event::AGES))
+ ->nullable()
+ ->rules(function () {
+ return [
+ function ($attribute, $value, $fail) {
+ if ($value === null || $value === '' || $value === [] || $value === '[]') {
+ return;
+ }
+ if (!is_array($value)) {
+ $decoded = json_decode((string) $value, true);
+ if (json_last_error() !== JSON_ERROR_NONE || !is_array($decoded)) {
+ $fail(__('validation.array', ['attribute' => $attribute]));
+ }
+ }
+ },
+ ];
+ })
+ ->fillUsing(function ($request, $model, $attribute, $requestAttribute) {
+ try {
+ $raw = $request->input($requestAttribute);
+ $decoded = is_array($raw) ? $raw : (is_string($raw) ? json_decode($raw, true) : null);
+ $model->{$attribute} = is_array($decoded) ? array_values($decoded) : [];
+ } catch (\Throwable $e) {
+ $model->{$attribute} = [];
+ }
+ })
+ ->hideFromIndex(),
+
+ // Flags
+ Boolean::make('Is Extracurricular Event', 'is_extracurricular_event')->hideFromIndex(),
+ Boolean::make('Is Standard School Curriculum', 'is_standard_school_curriculum')->hideFromIndex(),
+ Boolean::make('Is Using Resource', 'is_use_resource')->hideFromIndex(),
+
+ // Numbers / Stats
+ Number::make('Participants Count', 'participants_count')->step(1)->min(0)->hideFromIndex()->rules('nullable', 'integer', 'min:0'),
+ Number::make('Average Participant Age', 'average_participant_age')->step('any')->hideFromIndex()->rules('nullable', 'numeric', 'min:0'),
+ Number::make('Percentage of Females', 'percentage_of_females')->step('any')->hideFromIndex()->rules('nullable', 'numeric', 'min:0', 'max:100'),
+ Number::make('Males Count', 'males_count')->step(1)->min(0)->hideFromIndex()->rules('nullable', 'integer', 'min:0'),
+ Number::make('Females Count', 'females_count')->step(1)->min(0)->hideFromIndex()->rules('nullable', 'integer', 'min:0'),
+ Number::make('Other Count', 'other_count')->step(1)->min(0)->hideFromIndex()->rules('nullable', 'integer', 'min:0'),
+
+ // Codes / Misc
+ Text::make('Codeweek For All Participation Code', 'codeweek_for_all_participation_code')
+ ->onlyOnForms()->rules('nullable', 'max:255'),
+ Text::make('Name For Certificate', 'name_for_certificate')->onlyOnForms()->rules('nullable', 'max:255'),
+ Text::make('Language')->help('Comma-separated language codes (e.g., en,fr,de)')
+ ->onlyOnForms()->rules('nullable', 'max:255'),
+ Text::make('Source Ref', 'source_ref')->hideFromIndex()->rules('nullable', 'max:255'),
+
+ // Relations
+ BelongsTo::make('Owner', 'owner', \App\Nova\User::class)->searchable()->nullable(),
+ BelongsTo::make('Country Model', 'country', \App\Nova\Country::class)->onlyOnDetail(),
+
+ BelongsToMany::make('Audiences'),
+ BelongsToMany::make('Themes'),
+ BelongsToMany::make('Tags')->fields(function () {
+ return [];
+ }),
+
+ // Creator email shortcut for index
+ Text::make('Creator', 'owner.email')->onlyOnIndex(),
+
+ ];
+ } catch (\Throwable $e) {
+ return [
+ Text::make('Title')->sortable(),
+ Country::make('Country', 'country_iso')->sortable(),
+ Select::make('Status')->options([
+ 'APPROVED' => 'Approved',
+ 'REJECTED' => 'Rejected',
+ 'PENDING' => 'Pending',
+ ]),
+ ];
+ }
}
/**
diff --git a/app/Nova/Tag.php b/app/Nova/Tag.php
new file mode 100644
index 000000000..485e39bcb
--- /dev/null
+++ b/app/Nova/Tag.php
@@ -0,0 +1,24 @@
+sortable(),
+ Text::make('Name')->sortable()->rules('required', 'max:255'),
+ Text::make('Slug')->sortable()->rules('required', 'max:255'),
+ ];
+ }
+}
diff --git a/app/Nova/Theme.php b/app/Nova/Theme.php
new file mode 100644
index 000000000..75699f5ae
--- /dev/null
+++ b/app/Nova/Theme.php
@@ -0,0 +1,24 @@
+sortable(),
+ Text::make('Name')->sortable()->rules('required', 'max:255'),
+ Text::make('Order')->sortable()->rules('nullable', 'integer'),
+ ];
+ }
+}