Skip to content

Commit 78aadad

Browse files
committed
feat: add vcs instances functionality
1 parent 01eb90e commit 78aadad

8 files changed

Lines changed: 215 additions & 1 deletion

File tree

app/Enums/VcsPlatform.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,16 @@ public function getLabel(): string
1616
self::GITLAB => __('GitLab'),
1717
};
1818
}
19+
20+
/**
21+
* @return array<string, string>
22+
*/
23+
public static function getLabels(): array
24+
{
25+
$platforms = [];
26+
foreach (self::cases() as $platform) {
27+
$platforms[$platform->value] = $platform->getLabel();
28+
}
29+
return $platforms;
30+
}
1931
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Http\Controllers;
6+
7+
use App\Enums\VcsPlatform;
8+
use App\Http\Requests\VcsInstances\VcsInstanceCreateRequest;
9+
use App\Models\VcsInstance;
10+
use Illuminate\Http\RedirectResponse;
11+
use Inertia\Inertia;
12+
use Inertia\Response;
13+
14+
class VcsInstanceController extends Controller
15+
{
16+
public function create(): Response
17+
{
18+
return Inertia::render('vcsInstances/Create', [
19+
'platforms' => VcsPlatform::getLabels(),
20+
]);
21+
}
22+
23+
public function store(VcsInstanceCreateRequest $request): RedirectResponse
24+
{
25+
$vcsInstance = new VcsInstance($request->validated());
26+
$vcsInstance->installation_id = $vcsInstance->platform === VcsPlatform::GITHUB ? $vcsInstance->installation_id : null;
27+
$vcsInstance->token = $vcsInstance->platform === VcsPlatform::GITLAB ? $vcsInstance->token : null;
28+
$vcsInstance->save();
29+
30+
return to_route('repositories.index');
31+
}
32+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Http\Requests\VcsInstances;
6+
7+
use App\Models\VcsInstance;
8+
use Illuminate\Foundation\Http\FormRequest;
9+
10+
class VcsInstanceCreateRequest extends FormRequest
11+
{
12+
/**
13+
* Get the validation rules that apply to the request.
14+
*
15+
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
16+
*/
17+
public function rules(): array
18+
{
19+
return VcsInstance::rules();
20+
}
21+
}

app/Models/VcsInstance.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use Illuminate\Database\Eloquent\Factories\HasFactory;
1111
use Illuminate\Database\Eloquent\Model;
1212
use Illuminate\Database\Eloquent\Relations\HasMany;
13+
use Illuminate\Validation\Rule;
1314

1415
/**
1516
* Attributes
@@ -30,6 +31,14 @@ class VcsInstance extends Model
3031

3132
public $timestamps = false;
3233

34+
protected $fillable = [
35+
'name',
36+
'api_url',
37+
'token',
38+
'installation_id',
39+
'platform',
40+
];
41+
3342
protected function casts(): array
3443
{
3544
return [
@@ -38,6 +47,20 @@ protected function casts(): array
3847
];
3948
}
4049

50+
/**
51+
* @return array<string, array<array-key, mixed>>
52+
*/
53+
public static function rules(): array
54+
{
55+
return [
56+
'name' => ['required', 'string', 'max:255'],
57+
'api_url' => ['required', 'url', 'max:255'],
58+
'token' => ['required_if:platform,gitlab', 'nullable', 'string'],
59+
'installation_id' => ['required_if:platform,github', 'nullable', 'string', 'max:255'],
60+
'platform' => ['required', Rule::enum(VcsPlatform::class)],
61+
];
62+
}
63+
4164
/**
4265
* @return HasMany<Repository, $this>
4366
*/

resources/js/pages/repositories/Repositories.vue

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,13 @@ const props = defineProps<Props>();
2626

2727
<AppLayout :breadcrumbs="breadcrumbs">
2828
<div class="flex h-full flex-1 flex-col gap-4 rounded-xl px-4 py-6">
29-
<div class="flex justify-end">
29+
<div class="flex justify-end gap-2">
3030
<Link :href="route('repositories.create')" as="button">
3131
<Button><Plus /> Add repository</Button>
3232
</Link>
33+
<Link :href="route('vcs-instances.create')" as="button">
34+
<Button><Plus /> Add VCS instance</Button>
35+
</Link>
3336
</div>
3437
<div class="container mx-auto space-y-4 pb-10">
3538
<DataTable :columns="columns" :paginated-data="props.repositories" />
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
<script setup lang="ts">
2+
import Heading from '@/components/Heading.vue';
3+
import InputError from '@/components/InputError.vue';
4+
import { Button } from '@/components/ui/button';
5+
import { Input } from '@/components/ui/input';
6+
import { Label } from '@/components/ui/label';
7+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
8+
import AppLayout from '@/layouts/AppLayout.vue';
9+
import type { BreadcrumbItem, VcsInstance } from '@/types';
10+
import { Head, useForm } from '@inertiajs/vue3';
11+
12+
const breadcrumbs: BreadcrumbItem[] = [
13+
{
14+
title: 'Repositories',
15+
href: '/repositories',
16+
},
17+
{
18+
title: 'Add VCS instance',
19+
href: '#',
20+
},
21+
];
22+
23+
interface Props {
24+
platforms: Record<string, string>;
25+
}
26+
27+
defineProps<Props>();
28+
29+
const form = useForm<VcsInstance>({
30+
name: '',
31+
api_url: '',
32+
token: '',
33+
installation_id: '',
34+
platform: 'gitlab',
35+
});
36+
37+
const createVcsInstance = () => {
38+
form.post(route('vcs-instances.store'), {
39+
preserveScroll: true,
40+
});
41+
};
42+
</script>
43+
44+
<template>
45+
<AppLayout :breadcrumbs="breadcrumbs">
46+
<Head title="Add VCS instance" />
47+
48+
<div class="flex p-4">
49+
<div class="mx-auto w-full py-10 sm:w-1/2">
50+
<Heading title="Add VCS instance" />
51+
<form @submit.prevent="createVcsInstance" class="space-y-6">
52+
<div class="grid gap-2">
53+
<Label for="name">Name</Label>
54+
<Input id="name" v-model="form.name" class="mt-1 block w-full" required />
55+
<InputError :message="form.errors.name" />
56+
</div>
57+
58+
<div class="grid gap-2">
59+
<Label for="api_url">API URL</Label>
60+
<Input id="api_url" v-model="form.api_url" class="mt-1 block w-full" required />
61+
<InputError :message="form.errors.api_url" />
62+
<p class="text-sm text-muted-foreground">Base API URL e.g. https://api.github.com/ or https://gitlab.com/api/</p>
63+
</div>
64+
65+
<div class="grid gap-2">
66+
<Label for="platform">Platform</Label>
67+
<Select id="platform" v-model="form.platform" class="mt-1 block w-full" required>
68+
<SelectTrigger>
69+
<SelectValue placeholder="Select platform" />
70+
</SelectTrigger>
71+
<SelectContent>
72+
<SelectItem v-for="(label, value) in platforms" :key="value" :value="value">{{ label }}</SelectItem>
73+
</SelectContent>
74+
</Select>
75+
<InputError :message="form.errors.platform" />
76+
</div>
77+
78+
<div class="grid gap-2" v-if="form.platform === 'github'">
79+
<Label for="installation_id">Installation ID</Label>
80+
<Input id="installation_id" v-model="form.installation_id" class="mt-1 block w-full" required />
81+
<InputError :message="form.errors.installation_id" />
82+
<p class="text-sm text-muted-foreground">
83+
Installation ID could be found at Settings → Integrations → Applications → App as part of the URL
84+
</p>
85+
</div>
86+
87+
<div class="grid gap-2" v-if="form.platform === 'gitlab'">
88+
<Label for="token">Token</Label>
89+
<Input id="token" type="password" v-model="form.token" class="mt-1 block w-full" required />
90+
<InputError :message="form.errors.token" />
91+
</div>
92+
93+
<div class="flex items-center gap-4">
94+
<Button :disabled="form.processing">Create VCS instance</Button>
95+
96+
<Transition
97+
enter-active-class="transition ease-in-out"
98+
enter-from-class="opacity-0"
99+
leave-active-class="transition ease-in-out"
100+
leave-to-class="opacity-0"
101+
>
102+
<p v-show="form.recentlySuccessful" class="text-sm text-neutral-600">Saved.</p>
103+
</Transition>
104+
</div>
105+
</form>
106+
</div>
107+
</div>
108+
</AppLayout>
109+
</template>

resources/js/types/index.d.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,14 @@ export interface RepositoryForm {
8080
statistics_from?: string;
8181
}
8282

83+
export interface VcsInstance {
84+
name: string;
85+
api_url: string;
86+
token: string | null;
87+
installation_id: string | null;
88+
platform: 'github' | 'gitlab';
89+
}
90+
8391
export interface VcsInstanceUser {
8492
username: string;
8593
}

routes/web.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use App\Http\Controllers\DashboardController;
88
use App\Http\Controllers\RepositoryController;
99
use App\Http\Controllers\UserController;
10+
use App\Http\Controllers\VcsInstanceController;
1011
use App\Http\Controllers\VcsInstanceUserController;
1112
use Illuminate\Support\Facades\Route;
1213

@@ -54,6 +55,11 @@
5455
'destroy',
5556
]);
5657

58+
Route::resource('vcs-instances', VcsInstanceController::class)->only([
59+
'create',
60+
'store',
61+
]);
62+
5763
Route::resource('challenges', ChallengeController::class)->only([
5864
'create',
5965
'store',

0 commit comments

Comments
 (0)