Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
68 changes: 68 additions & 0 deletions app/GrassrootsGrantsHub.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;

class GrassrootsGrantsHub extends Model
{
protected $table = 'grassroots_grants_hubs';

protected $fillable = [
'page_id',
'title',
'hub_status',
'projects_funded',
'participants_reached',
'educators_engaged',
'activities_on_platform',
'overview',
'underserved_focus',
'status_message',
'position',
'active',
'image_folder',
];

protected $casts = [
'projects_funded' => 'integer',
'participants_reached' => 'integer',
'educators_engaged' => 'integer',
'activities_on_platform' => 'integer',
'position' => 'integer',
'active' => 'boolean',
];

protected static function booted(): void
{
static::creating(function (self $hub) {
if ($hub->position === null || $hub->position === 0) {
$pageId = $hub->page_id ?: (int) (request()->query('viaResourceId') ?? 1);
$max = self::where('page_id', $pageId)->max('position');
$hub->position = $max === null ? 0 : (int) $max + 1;
}
});
}

public function page(): BelongsTo
{
return $this->belongsTo(GrassrootsGrantsPage::class, 'page_id');
}

public function projects(): HasMany
{
return $this->hasMany(GrassrootsGrantsProject::class, 'hub_id')->orderBy('position');
}

public function activeProjects(): HasMany
{
return $this->projects()->where('active', true);
}

public function isStatusOnly(): bool
{
return in_array($this->hub_status, ['not_launched', 'cancelled'], true);
}
}
56 changes: 56 additions & 0 deletions app/GrassrootsGrantsPage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;

class GrassrootsGrantsPage extends Model
{
protected $table = 'grassroots_grants_page';

protected $fillable = [
'is_preview_mode',
'hero_title',
'hero_subtitle',
'hero_image',
'meta_title',
'meta_description',
'round_title',
'overview_intro',
'overview_activity_types',
'overview_underserved',
];

protected $casts = [
'is_preview_mode' => 'boolean',
];

public function hubs(): HasMany
{
return $this->hasMany(GrassrootsGrantsHub::class, 'page_id')->orderBy('position');
}

public function activeHubs(): HasMany
{
return $this->hubs()->where('active', true);
}

public static function config(): self
{
$page = self::first();
if ($page) {
return $page;
}

return self::create([
'is_preview_mode' => true,
'hero_title' => 'EU Code Week Grants for Grassroots',
'hero_subtitle' => 'Round 1 project highlights from grassroots coding initiatives across Europe.',
'hero_image' => '/images/contact-us.png',
'meta_title' => 'Grassroots Grants – EU Code Week',
'meta_description' => 'Discover EU Code Week Round 1 grassroots grant projects, impact, and funded initiatives across Europe.',
'round_title' => 'Round 1 of Grants',
]);
}
}
58 changes: 58 additions & 0 deletions app/GrassrootsGrantsProject.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;

class GrassrootsGrantsProject extends Model
{
protected $table = 'grassroots_grants_projects';

protected $fillable = [
'hub_id',
'title',
'organisation',
'location',
'participants',
'educators',
'activities',
'description',
'underserved_focus',
'position',
'active',
'image_folder',
];

protected $casts = [
'position' => 'integer',
'active' => 'boolean',
];

protected static function booted(): void
{
static::creating(function (self $project) {
if ($project->position === null || $project->position === 0) {
$hubId = $project->hub_id ?: (int) (request()->query('viaResourceId') ?? 0);
$max = self::where('hub_id', $hubId)->max('position');
$project->position = $max === null ? 0 : (int) $max + 1;
}
});
}

public function hub(): BelongsTo
{
return $this->belongsTo(GrassrootsGrantsHub::class, 'hub_id');
}

public function links(): HasMany
{
return $this->hasMany(GrassrootsGrantsProjectLink::class, 'project_id')->orderBy('position');
}

public function images(): HasMany
{
return $this->hasMany(GrassrootsGrantsProjectImage::class, 'project_id')->orderBy('position');
}
}
64 changes: 64 additions & 0 deletions app/GrassrootsGrantsProjectImage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class GrassrootsGrantsProjectImage extends Model
{
protected $table = 'grassroots_grants_project_images';

protected $fillable = [
'project_id',
'url',
'alt',
'file_type',
'position',
];

protected $casts = [
'position' => 'integer',
];

protected static function booted(): void
{
static::creating(function (self $image) {
if ($image->position === null || $image->position === 0) {
$projectId = $image->project_id ?: (int) (request()->query('viaResourceId') ?? 0);
$max = self::where('project_id', $projectId)->max('position');
$image->position = $max === null ? 0 : (int) $max + 1;
}
});
}

public function project(): BelongsTo
{
return $this->belongsTo(GrassrootsGrantsProject::class, 'project_id');
}

public function resolvedUrl(): string
{
$url = trim((string) $this->url);
if ($url === '') {
return '';
}

if (str_starts_with($url, 'http://') || str_starts_with($url, 'https://') || str_starts_with($url, '//')) {
return $url;
}

$path = str_starts_with($url, '/') ? $url : '/'.$url;
$segments = explode('/', $path);
$encoded = array_map(static function (string $segment): string {
return rawurlencode(rawurldecode($segment));
}, $segments);

return implode('/', $encoded);
}

public function isPdf(): bool
{
return $this->file_type === 'pdf' || str_ends_with(strtolower($this->url), '.pdf');
}
}
38 changes: 38 additions & 0 deletions app/GrassrootsGrantsProjectLink.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class GrassrootsGrantsProjectLink extends Model
{
protected $table = 'grassroots_grants_project_links';

protected $fillable = [
'project_id',
'label',
'url',
'position',
];

protected $casts = [
'position' => 'integer',
];

protected static function booted(): void
{
static::creating(function (self $link) {
if ($link->position === null || $link->position === 0) {
$projectId = $link->project_id ?: (int) (request()->query('viaResourceId') ?? 0);
$max = self::where('project_id', $projectId)->max('position');
$link->position = $max === null ? 0 : (int) $max + 1;
}
});
}

public function project(): BelongsTo
{
return $this->belongsTo(GrassrootsGrantsProject::class, 'project_id');
}
}
26 changes: 26 additions & 0 deletions app/Http/View/Composers/GrassrootsGrantsComposer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

namespace App\Http\View\Composers;

use App\GrassrootsGrantsPage;
use Illuminate\Support\Facades\Schema;
use Illuminate\View\View;

class GrassrootsGrantsComposer
{
public function compose(View $view): void
{
if (! Schema::hasTable('grassroots_grants_page')) {
$view->with('page', null);

return;
}

$page = GrassrootsGrantsPage::config()->load([
'activeHubs.activeProjects.links',
'activeHubs.activeProjects.images',
]);

$view->with('page', $page);
}
}
78 changes: 78 additions & 0 deletions app/Nova/GrassrootsGrantsHub.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<?php

namespace App\Nova;

use Illuminate\Http\Request;
use Laravel\Nova\Fields\Boolean;
use Laravel\Nova\Fields\HasMany;
use Laravel\Nova\Fields\Hidden;
use Laravel\Nova\Fields\ID;
use Laravel\Nova\Fields\Number;
use Laravel\Nova\Fields\Select;
use Laravel\Nova\Fields\Text;
use Laravel\Nova\Fields\Trix;
use Laravel\Nova\Http\Requests\NovaRequest;

class GrassrootsGrantsHub extends Resource
{
public static $group = 'Content';

public static $model = \App\GrassrootsGrantsHub::class;

public static $title = 'title';

public static $search = ['title', 'overview'];

public static $displayInNavigation = false;

public static function label(): string
{
return 'Grassroots Grants Hubs';
}

public static function singularLabel(): string
{
return 'Country Hub';
}

public function fields(Request $request): array
{
return [
ID::make()->onlyOnForms(),
Hidden::make('Page', 'page_id')
->default(function ($request) {
if ($request instanceof NovaRequest && method_exists($request, 'viaResourceId')) {
return $request->viaResourceId() ?? 1;
}

return $request->query('viaResourceId', 1);
}),
Text::make('Title', 'title')->rules('required', 'string'),
Select::make('Status', 'hub_status')->options([
'active' => 'Active (funded projects)',
'not_launched' => 'Call not launched',
'cancelled' => 'Cancelled',
])->displayUsingLabels(),
Number::make('Projects funded', 'projects_funded')->nullable()->min(0),
Number::make('Participants reached', 'participants_reached')->nullable()->min(0),
Number::make('Educators engaged', 'educators_engaged')->nullable()->min(0),
Number::make('Activities on platform', 'activities_on_platform')->nullable()->min(0),
Trix::make('Overview', 'overview')->nullable(),
Text::make('Underserved focus', 'underserved_focus')->nullable(),
Trix::make('Status message', 'status_message')
->nullable()
->help('Shown for hubs where the call was not launched or was cancelled.'),
Text::make('Image folder', 'image_folder')
->nullable()
->help('Relative to public/images/grants/ for reference.'),
Boolean::make('Active', 'active')->default(true),
Number::make('Order', 'position')->min(0),
HasMany::make('Funded projects', 'projects', GrassrootsGrantsProject::class),
];
}

public static function indexQuery(NovaRequest $request, $query)
{
return $query->orderBy('position');
}
}
Loading
Loading