diff --git a/app/Http/Controllers/V5/ApplicationController.php b/app/Http/Controllers/V5/ApplicationController.php
index 8d11cd6b6..ca1a4c636 100644
--- a/app/Http/Controllers/V5/ApplicationController.php
+++ b/app/Http/Controllers/V5/ApplicationController.php
@@ -51,6 +51,7 @@ public function __construct(private readonly ConnectionFirewallSync $firewallSyn
public function store(Request $request): JsonResponse
{
$currentTeam = $this->currentTeamOrFail($request);
+ $this->authorize('create', [V5Application::class, $currentTeam]);
$projects = $this->projects($currentTeam);
[$selectedProject, $selectedEnvironment] = $this->selectedProjectAndEnvironment($request, $projects);
diff --git a/app/Http/Controllers/V5/Concerns/ResolvesProjectSelection.php b/app/Http/Controllers/V5/Concerns/ResolvesProjectSelection.php
index 1d48eff33..a741d7f8f 100644
--- a/app/Http/Controllers/V5/Concerns/ResolvesProjectSelection.php
+++ b/app/Http/Controllers/V5/Concerns/ResolvesProjectSelection.php
@@ -34,8 +34,8 @@ protected function serializeCurrentTeam(mixed $currentTeam): ?array
*/
protected function selectedProjectAndEnvironment(Request $request, array $projects): array
{
- $selectedProjectUuid = $request->session()->get(self::SELECTED_PROJECT_SESSION_KEY);
- $selectedEnvironmentUuid = $request->session()->get(self::SELECTED_ENVIRONMENT_SESSION_KEY);
+ $selectedProjectUuid = $request->query('project', $request->session()->get(self::SELECTED_PROJECT_SESSION_KEY));
+ $selectedEnvironmentUuid = $request->query('environment', $request->session()->get(self::SELECTED_ENVIRONMENT_SESSION_KEY));
$selectedProject = null;
foreach ($projects as $project) {
@@ -59,6 +59,13 @@ protected function selectedProjectAndEnvironment(Request $request, array $projec
$selectedEnvironment ??= $selectedProject['environments'][0] ?? null;
+ if ($request->query->has('project') || $request->query->has('environment')) {
+ $request->session()->put([
+ self::SELECTED_PROJECT_SESSION_KEY => $selectedProject['uuid'] ?? null,
+ self::SELECTED_ENVIRONMENT_SESSION_KEY => $selectedEnvironment['uuid'] ?? null,
+ ]);
+ }
+
return [$selectedProject, $selectedEnvironment];
}
diff --git a/app/Http/Controllers/V5/DashboardController.php b/app/Http/Controllers/V5/DashboardController.php
index 9f5e26736..de7176875 100644
--- a/app/Http/Controllers/V5/DashboardController.php
+++ b/app/Http/Controllers/V5/DashboardController.php
@@ -33,17 +33,23 @@ public function __invoke(Request $request, FluxHealth $fluxHealth): Response
$currentTeam = $request->attributes->get('v5.currentTeam');
$projects = $this->projects($currentTeam);
[$selectedProject, $selectedEnvironment] = $this->selectedProjectAndEnvironment($request, $projects);
+ $applications = $this->applications($currentTeam, $selectedProject, $selectedEnvironment);
+ $requestedApplicationUuid = $request->query('application');
+ $selectedApplicationUuid = collect($applications)->contains(
+ fn (array $application): bool => $application['id'] === $requestedApplicationUuid
+ ) ? $requestedApplicationUuid : null;
return Inertia::render('Dashboard', [
'currentTeam' => $this->serializeCurrentTeam($currentTeam),
'flux' => $fluxHealth->check(),
- 'applications' => $this->applications($currentTeam, $selectedProject, $selectedEnvironment),
+ 'applications' => $applications,
'caddyIngresses' => $this->caddyIngresses($currentTeam),
'resourceConnections' => $this->resourceConnections($currentTeam, $selectedProject, $selectedEnvironment),
'nginxServers' => $this->nginxServers($currentTeam),
'projects' => $projects,
'selectedProjectUuid' => $selectedProject['uuid'] ?? null,
'selectedEnvironmentUuid' => $selectedEnvironment['uuid'] ?? null,
+ 'selectedApplicationUuid' => $selectedApplicationUuid,
]);
}
diff --git a/app/Http/Controllers/V5/ResourceConnectionController.php b/app/Http/Controllers/V5/ResourceConnectionController.php
index d901940d6..299432e83 100644
--- a/app/Http/Controllers/V5/ResourceConnectionController.php
+++ b/app/Http/Controllers/V5/ResourceConnectionController.php
@@ -35,6 +35,7 @@ public function __construct(
public function store(Request $request): JsonResponse
{
$currentTeam = $this->currentTeamOrFail($request);
+ $this->authorize('create', [ResourceConnection::class, $currentTeam]);
$projects = $this->projects($currentTeam);
[$selectedProject, $selectedEnvironment] = $this->selectedProjectAndEnvironment($request, $projects);
diff --git a/app/Livewire/Project/Resource/Index.php b/app/Livewire/Project/Resource/Index.php
index 094b61b28..acfa1c03f 100644
--- a/app/Livewire/Project/Resource/Index.php
+++ b/app/Livewire/Project/Resource/Index.php
@@ -4,6 +4,7 @@
use App\Models\Environment;
use App\Models\Project;
+use App\Models\V5\Application as V5Application;
use Illuminate\Support\Collection;
use Livewire\Component;
@@ -61,6 +62,7 @@ public function mount(): void
->select('id', 'uuid', 'name', 'project_id')
->with([
'applications:id,uuid,name,environment_id',
+ 'v5Applications:id,uuid,name,environment_id,status',
'services:id,uuid,name,environment_id',
'postgresqls:id,uuid,name,environment_id',
'redis:id,uuid,name,environment_id',
@@ -103,6 +105,23 @@ public function mount(): void
return $application;
});
+ $this->applications = $this->applications
+ ->merge(V5Application::query()
+ ->where('team_id', currentTeam()->id)
+ ->where('project_id', $this->project->id)
+ ->where('environment_id', $this->environment->id)
+ ->with('server:id,name')
+ ->get()
+ ->map(function (V5Application $application) use ($projectUuid, $environmentUuid) {
+ $application->hrefLink = route('v5.dashboard', [
+ 'project' => $projectUuid,
+ 'environment' => $environmentUuid,
+ 'application' => $application->uuid,
+ ]);
+
+ return $application;
+ }))
+ ->sortBy('name');
// Load all database resources in a single query per type
$databaseTypes = [
@@ -180,16 +199,19 @@ private function toSearchableArray(Collection $items): array
'uuid' => $item->uuid,
'name' => $item->name,
'fqdn' => $item->fqdn ?? null,
- 'description' => $item->description ?? null,
+ 'description' => $item instanceof V5Application ? 'Managed by Coolify V5' : ($item->description ?? null),
'status' => $item->status ?? '',
+ 'version' => $item instanceof V5Application ? 'v5' : 'v4',
'server_status' => $item->server_status ?? null,
'hrefLink' => $item->hrefLink ?? '',
'destination' => [
'server' => [
- 'name' => $item->destination?->server?->name ?? 'Unknown',
+ 'name' => $item instanceof V5Application
+ ? ($item->server?->name ?? 'Unknown')
+ : ($item->destination?->server?->name ?? 'Unknown'),
],
],
- 'tags' => $item->tags->map(fn ($tag) => [
+ 'tags' => ($item instanceof V5Application ? collect() : $item->tags)->map(fn ($tag) => [
'id' => $tag->id,
'name' => $tag->name,
])->values()->toArray(),
diff --git a/app/Models/Environment.php b/app/Models/Environment.php
index 1364d874a..0e5e3c673 100644
--- a/app/Models/Environment.php
+++ b/app/Models/Environment.php
@@ -2,6 +2,8 @@
namespace App\Models;
+use App\Models\V5\Application as V5Application;
+use App\Models\V5\ResourceConnection as V5ResourceConnection;
use App\Traits\ClearsGlobalSearchCache;
use App\Traits\HasSafeStringAttribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
@@ -54,7 +56,9 @@ public static function ownedByCurrentTeamAPI(int $teamId)
public function isEmpty()
{
- return $this->applications()->count() == 0 &&
+ return ! V5Application::query()->where('environment_id', $this->id)->exists() &&
+ ! V5ResourceConnection::query()->where('environment_id', $this->id)->exists() &&
+ $this->applications()->count() == 0 &&
$this->redis()->count() == 0 &&
$this->postgresqls()->count() == 0 &&
$this->mysqls()->count() == 0 &&
@@ -76,6 +80,11 @@ public function applications()
return $this->hasMany(Application::class);
}
+ public function v5Applications()
+ {
+ return $this->hasMany(V5Application::class);
+ }
+
public function postgresqls()
{
return $this->hasMany(StandalonePostgresql::class);
diff --git a/app/Models/Project.php b/app/Models/Project.php
index 5c821b017..fe64e320d 100644
--- a/app/Models/Project.php
+++ b/app/Models/Project.php
@@ -2,6 +2,8 @@
namespace App\Models;
+use App\Models\V5\Application as V5Application;
+use App\Models\V5\ResourceConnection as V5ResourceConnection;
use App\Traits\ClearsGlobalSearchCache;
use App\Traits\HasSafeStringAttribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
@@ -144,7 +146,9 @@ public function mariadbs()
public function isEmpty()
{
- return $this->applications()->count() == 0 &&
+ return ! V5Application::query()->where('project_id', $this->id)->exists() &&
+ ! V5ResourceConnection::query()->where('project_id', $this->id)->exists() &&
+ $this->applications()->count() == 0 &&
$this->redis()->count() == 0 &&
$this->postgresqls()->count() == 0 &&
$this->mysqls()->count() == 0 &&
diff --git a/app/Policies/V5/ApplicationPolicy.php b/app/Policies/V5/ApplicationPolicy.php
index 5b1877a00..aca24aa95 100644
--- a/app/Policies/V5/ApplicationPolicy.php
+++ b/app/Policies/V5/ApplicationPolicy.php
@@ -9,6 +9,13 @@
class ApplicationPolicy
{
+ public function create(User $user, Team $team): Response
+ {
+ return $user->isAdminOfTeam($team->id)
+ ? Response::allow()
+ : Response::deny('You do not have permission to manage applications in this team.');
+ }
+
/**
* Determine whether the user can view the application within the current team.
*
diff --git a/app/Policies/V5/ResourceConnectionPolicy.php b/app/Policies/V5/ResourceConnectionPolicy.php
index 6b3dcde79..04e472e8e 100644
--- a/app/Policies/V5/ResourceConnectionPolicy.php
+++ b/app/Policies/V5/ResourceConnectionPolicy.php
@@ -9,6 +9,13 @@
class ResourceConnectionPolicy
{
+ public function create(User $user, Team $team): Response
+ {
+ return $user->isAdminOfTeam($team->id)
+ ? Response::allow()
+ : Response::deny('You do not have permission to manage resource connections in this team.');
+ }
+
/**
* Determine whether the user can update the connection within the current team.
*/
diff --git a/other/nightly/install.sh b/other/nightly/install.sh
index f4458d440..33437aad5 100755
--- a/other/nightly/install.sh
+++ b/other/nightly/install.sh
@@ -228,11 +228,12 @@ if [ "$WARNING_SPACE" = true ]; then
sleep 5
fi
-mkdir -p /data/coolify/{source,ssh,applications,databases,backups,services,proxy,sentinel}
+mkdir -p /data/coolify/{source,ssh,applications,databases,backups,services,proxy,sentinel,flux}
mkdir -p /data/coolify/ssh/{keys,mux}
mkdir -p /data/coolify/proxy/dynamic
chown -R 9999:root /data/coolify
+chown -R 9999:root /data/coolify/flux
chmod -R 700 /data/coolify
INSTALLATION_LOG_WITH_DATE="/data/coolify/source/installation-${DATE}.log"
diff --git a/other/nightly/upgrade.sh b/other/nightly/upgrade.sh
index 635ddd77c..a15ec39a8 100644
--- a/other/nightly/upgrade.sh
+++ b/other/nightly/upgrade.sh
@@ -171,6 +171,9 @@ else
log "Network 'coolify' already exists"
fi
+mkdir -p /data/coolify/flux
+chown -R 9999:root /data/coolify/flux
+
# Check if Docker config file exists
DOCKER_CONFIG_MOUNT=""
if [ -f /root/.docker/config.json ]; then
diff --git a/package-lock.json b/package-lock.json
index 6422dd492..9b050a8a7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -135,7 +135,6 @@
"integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@babel/code-frame": "^7.29.7",
"@babel/generator": "^7.29.7",
@@ -740,7 +739,6 @@
}
],
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=20.19.0"
},
@@ -789,7 +787,6 @@
}
],
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=20.19.0"
}
@@ -997,12 +994,36 @@
"@noble/ciphers": "^1.0.0"
}
},
+ "node_modules/@emnapi/core": {
+ "version": "1.11.2",
+ "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.2.tgz",
+ "integrity": "sha512-TC8MkTuZUtcTSiFeuC0ksCh9QIJ5+F21MvZ4Wn4ORfYaFJ/0dsiudv5tVkejgwZlwQ39jL9WWDe2lz8x0WglOA==",
+ "license": "MIT",
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "@emnapi/wasi-threads": "1.2.2",
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/runtime": {
+ "version": "1.11.2",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.2.tgz",
+ "integrity": "sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==",
+ "license": "MIT",
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
"node_modules/@emnapi/wasi-threads": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz",
"integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==",
"license": "MIT",
"optional": true,
+ "peer": true,
"dependencies": {
"tslib": "^2.4.0"
}
@@ -1244,7 +1265,6 @@
"integrity": "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==",
"dev": true,
"license": "MIT",
- "peer": true,
"engines": {
"node": "^14.21.3 || >=16"
},
@@ -2082,7 +2102,8 @@
"resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz",
"integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==",
"dev": true,
- "license": "MIT"
+ "license": "MIT",
+ "peer": true
},
"node_modules/@types/babel__core": {
"version": "7.20.5",
@@ -2167,7 +2188,6 @@
"integrity": "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==",
"devOptional": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"csstype": "^3.2.2"
}
@@ -2178,7 +2198,6 @@
"integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
"dev": true,
"license": "MIT",
- "peer": true,
"peerDependencies": {
"@types/react": "^19.2.0"
}
@@ -2337,8 +2356,7 @@
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.5.0.tgz",
"integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==",
- "license": "MIT",
- "peer": true
+ "license": "MIT"
},
"node_modules/accepts": {
"version": "2.0.0",
@@ -2425,6 +2443,7 @@
"integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
"dev": true,
"license": "MIT",
+ "peer": true,
"engines": {
"node": ">=10"
},
@@ -2445,6 +2464,7 @@
"integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==",
"dev": true,
"license": "Apache-2.0",
+ "peer": true,
"dependencies": {
"dequal": "^2.0.3"
}
@@ -2600,7 +2620,6 @@
}
],
"license": "MIT",
- "peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.10.12",
"caniuse-lite": "^1.0.30001782",
@@ -3172,6 +3191,7 @@
"integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
"dev": true,
"license": "MIT",
+ "peer": true,
"engines": {
"node": ">=6"
}
@@ -3200,7 +3220,8 @@
"resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz",
"integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==",
"dev": true,
- "license": "MIT"
+ "license": "MIT",
+ "peer": true
},
"node_modules/dot-prop": {
"version": "6.0.1",
@@ -3523,7 +3544,6 @@
"integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"accepts": "^2.0.0",
"body-parser": "^2.2.1",
@@ -3973,7 +3993,6 @@
"integrity": "sha512-2NFaIyNVgJmBs/ecmtGzlmluTFs5cHEWGTdu0t1HBwYzoGXOL5nUQBRMXsXWla5i4KkG//QMzVP88m1+I3fdAQ==",
"dev": true,
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=16.9.0"
}
@@ -4871,6 +4890,7 @@
"integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==",
"dev": true,
"license": "MIT",
+ "peer": true,
"bin": {
"lz-string": "bin/bin.js"
}
@@ -5622,6 +5642,7 @@
"integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"ansi-regex": "^5.0.1",
"ansi-styles": "^5.0.0",
@@ -5777,7 +5798,6 @@
"resolved": "https://registry.npmjs.org/react/-/react-19.2.7.tgz",
"integrity": "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==",
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=0.10.0"
}
@@ -5787,7 +5807,6 @@
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.7.tgz",
"integrity": "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==",
"license": "MIT",
- "peer": true,
"dependencies": {
"scheduler": "^0.27.0"
},
@@ -5800,7 +5819,8 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
"dev": true,
- "license": "MIT"
+ "license": "MIT",
+ "peer": true
},
"node_modules/react-refresh": {
"version": "0.18.0",
@@ -6501,8 +6521,7 @@
"version": "4.1.18",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz",
"integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==",
- "license": "MIT",
- "peer": true
+ "license": "MIT"
},
"node_modules/tapable": {
"version": "2.3.0",
@@ -6718,7 +6737,6 @@
"integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==",
"dev": true,
"license": "Apache-2.0",
- "peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -6841,7 +6859,6 @@
"resolved": "https://registry.npmjs.org/vite/-/vite-8.0.16.tgz",
"integrity": "sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==",
"license": "MIT",
- "peer": true,
"dependencies": {
"lightningcss": "^1.32.0",
"picomatch": "^4.0.4",
@@ -7465,7 +7482,6 @@
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
"dev": true,
"license": "MIT",
- "peer": true,
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
diff --git a/resources/js/v5/Pages/Dashboard.tsx b/resources/js/v5/Pages/Dashboard.tsx
index 3ee7522ed..c1f7db12c 100644
--- a/resources/js/v5/Pages/Dashboard.tsx
+++ b/resources/js/v5/Pages/Dashboard.tsx
@@ -90,6 +90,7 @@ export default function Dashboard({
projects = [],
selectedProjectUuid = null,
selectedEnvironmentUuid = null,
+ selectedApplicationUuid = null,
}: V5DashboardProps) {
const [applications, setApplications] = useState