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
3 changes: 2 additions & 1 deletion app/Filament/Resources/CompanyJobs/CompanyJobResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use App\Filament\Resources\CompanyJobs\Pages\CreateCompanyJob;
use App\Filament\Resources\CompanyJobs\Pages\EditCompanyJob;
use App\Filament\Resources\CompanyJobs\Pages\ListCompanyJobs;
use App\Filament\Resources\CompanyJobs\RelationManagers\ApplicationsRelationManager;
use App\Filament\Resources\CompanyJobs\Schemas\CompanyJobForm;
use App\Filament\Resources\CompanyJobs\Tables\CompanyJobsTable;
use App\Models\CompanyJob;
Expand Down Expand Up @@ -39,7 +40,7 @@ public static function table(Table $table): Table
public static function getRelations(): array
{
return [
//
ApplicationsRelationManager::class,
];
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<?php

namespace App\Filament\Resources\CompanyJobs\RelationManagers;

use App\Enums\ApplicationStatus;
use App\Models\CompanyJobApplication;
use App\Models\Scopes\ApprovedScope;
use Filament\Resources\RelationManagers\RelationManager;
use Filament\Schemas\Schema;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;

class ApplicationsRelationManager extends RelationManager
{
protected static string $relationship = 'applications';

protected static ?string $title = 'Participants';

public function form(Schema $schema): Schema
{
return $schema->components([]);
}

public function table(Table $table): Table
{
return $table
->recordTitleAttribute('id')
->modifyQueryUsing(function (Builder $query): Builder {
return $query->with([
'developer' => fn ($q) => $q
->withoutGlobalScope(ApprovedScope::class)
->with('user'),
]);
})
->columns([
TextColumn::make('developer.name')
->label('Developer')
->sortable()
->placeholder('—'),
TextColumn::make('participant_email')
->label('Email')
->state(function (CompanyJobApplication $record): string {
$developer = $record->developer;

if ($developer === null) {
return '—';
}

return $developer->user?->email
?? $developer->email
?? '—';
}),
TextColumn::make('status')
->badge()
->formatStateUsing(fn (ApplicationStatus $state): string => $state->getLabel())
->color(fn (ApplicationStatus $state): string => $state->getColor())
Comment on lines +56 to +57
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The closures for formatStateUsing and color use a non-nullable type hint for ApplicationStatus. If the status field is null in the database, this will trigger a TypeError. It is safer to use a nullable type hint and handle the null case with the null-safe operator or a fallback.

                    ->formatStateUsing(fn (?ApplicationStatus $state): string => $state?->getLabel() ?? '')
                    ->color(fn (?ApplicationStatus $state): ?string => $state?->getColor())

->sortable(),
TextColumn::make('cover_message')
->label('Message')
->limit(80)
->wrap()
->placeholder('—')
->toggleable(),
TextColumn::make('created_at')
->label('Applied')
->dateTime()
->sortable(),
])
->defaultSort('created_at', 'desc')
->filters([
//
])
->headerActions([
//
])
->recordActions([
//
])
->toolbarActions([
//
]);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ public static function configure(Table $table): Table
IconColumn::make('first_payment_qi_confirmed')
->label('Qi paid')
->boolean(),
TextColumn::make('gitea_repository_url')
->label('Gitea repository')
->url(fn (?string $state): ?string => $state)
->openUrlInNewTab()
->placeholder('—'),
TextColumn::make('gitea_owner')
->label('Gitea owner')
->placeholder('—')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public function index(Request $request): Response
'application_id' => $app->id,
'job_title' => $app->companyJob->title,
'job_slug' => $app->companyJob->slug,
'repo_url' => $app->companyJob->giteaRepositoryWebUrl(),
'repo_url' => $app->companyJob->gitea_repository_url,
'gitea_owner' => $app->companyJob->gitea_owner,
'gitea_repo_name' => $app->companyJob->gitea_repo_name,
'provisioned_at' => $app->companyJob->gitea_provisioned_at?->toIso8601String(),
Expand Down
21 changes: 12 additions & 9 deletions app/Models/CompanyJob.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use App\Enums\JobStatus;
use App\Enums\WorldGovernorate;
use Database\Factories\CompanyJobFactory;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
Expand Down Expand Up @@ -108,18 +109,20 @@ public function scopeRejected($query)
/**
* Web UI URL for this job's Gitea repository, or null if not provisioned or Gitea base URL is unset.
*/
public function giteaRepositoryWebUrl(): ?string
protected function giteaRepositoryUrl(): Attribute
{
if ($this->gitea_owner === null || $this->gitea_repo_name === null) {
return null;
}
return Attribute::get(function (): ?string {
if ($this->gitea_owner === null || $this->gitea_repo_name === null) {
return null;
}

$base = rtrim((string) config('services.gitea.url'), '/');
if ($base === '') {
return null;
}
$base = rtrim((string) config('services.gitea.url'), '/');
if ($base === '') {
return null;
}

return $base.'/'.rawurlencode((string) $this->gitea_owner).'/'.rawurlencode((string) $this->gitea_repo_name);
return $base.'/'.rawurlencode((string) $this->gitea_owner).'/'.rawurlencode((string) $this->gitea_repo_name);
});
}

public function getActivitylogOptions(): LogOptions
Expand Down
16 changes: 15 additions & 1 deletion app/Services/CompanyJobGiteaProvisioner.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public function provisionRepositoryForApprovedJob(CompanyJob $job): void
throw new RuntimeException('Could not determine Gitea username for the post owner.');
}

$repoName = 'remote-work-'.$job->id;
$repoName = $this->repositoryNameForJob($job);

$this->gitea->createRepositoryForUser(
$login,
Expand Down Expand Up @@ -129,4 +129,18 @@ public function addAcceptedApplication(CompanyJobApplication $application): void

$this->addDeveloperToJobRepository($job, $developer);
}

/**
* Gitea repository name matches the job's public slug, max 100 chars per API limits.
*/
private function repositoryNameForJob(CompanyJob $job): string
{
$slug = trim((string) $job->slug);

if ($slug === '') {
return 'remote-work-'.$job->id;
}

return mb_substr($slug, 0, 100);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Truncating the slug to 100 characters might lead to repository name collisions if multiple jobs have long slugs that are identical for the first 100 characters. While 100 characters is a generous limit, appending the job ID (or a portion of it) would guarantee uniqueness even after truncation.

        return mb_substr($slug, 0, 90) . '-' . $job->id;

}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading