Skip to content
Open
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
22 changes: 14 additions & 8 deletions app/api/applications/[id]/environments/[envId]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,13 @@ export async function PUT(

if (!response.ok) {
const errorText = await response.text();
return NextResponse.json(
{ error: errorText || "Failed to update environment" },
{ status: response.status }
);
let errorData;
try {
errorData = JSON.parse(errorText);
} catch (e) {
errorData = { error: errorText || "Failed to update environment" };
}
return NextResponse.json(errorData, { status: response.status });
}

const data = await response.json();
Expand All @@ -73,10 +76,13 @@ export async function DELETE(

if (!response.ok) {
const errorText = await response.text();
return NextResponse.json(
{ error: errorText || "Failed to delete environment" },
{ status: response.status }
);
let errorData;
try {
errorData = JSON.parse(errorText);
} catch (e) {
errorData = { error: errorText || "Failed to delete environment" };
}
return NextResponse.json(errorData, { status: response.status });
}

return new NextResponse(null, { status: 204 });
Expand Down
22 changes: 14 additions & 8 deletions app/api/applications/[id]/environments/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@ export async function GET(
const response = await fetchBackend(`/v1/applications/${id}/environments?size=50`);
if (!response.ok) {
const errorText = await response.text();
return NextResponse.json(
{ error: errorText || "Failed to fetch environments" },
{ status: response.status }
);
let errorData;
try {
errorData = JSON.parse(errorText);
} catch (e) {
errorData = { error: errorText || "Failed to fetch environments" };
}
return NextResponse.json(errorData, { status: response.status });
}

const data = await response.json();
Expand Down Expand Up @@ -44,10 +47,13 @@ export async function POST(

if (!response.ok) {
const errorText = await response.text();
return NextResponse.json(
{ error: errorText || "Failed to create environment" },
{ status: response.status }
);
let errorData;
try {
errorData = JSON.parse(errorText);
} catch (e) {
errorData = { error: errorText || "Failed to create environment" };
}
return NextResponse.json(errorData, { status: response.status });
}

const data = await response.json();
Expand Down
11 changes: 7 additions & 4 deletions app/api/applications/[id]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,13 @@ export async function DELETE(

if (!response.ok) {
const errorText = await response.text();
return NextResponse.json(
{ error: errorText || "Failed to delete application" },
{ status: response.status }
);
let errorData;
try {
errorData = JSON.parse(errorText);
} catch (e) {
errorData = { error: errorText || "Failed to delete application" };
}
return NextResponse.json(errorData, { status: response.status });
}

return new NextResponse(null, { status: 204 });
Expand Down
2 changes: 2 additions & 0 deletions app/api/applications/route.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { NextRequest, NextResponse } from "next/server";
import { fetchBackend } from "@/lib/api";

export const dynamic = "force-dynamic";

export async function GET(request: NextRequest) {
try {
const { searchParams } = new URL(request.url);
Expand Down
69 changes: 61 additions & 8 deletions app/dashboard/applications/[id]/settings/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export default function ApplicationSettingsPage({
const [isLoading, setIsLoading] = useState(true)
const [isSaving, setIsSaving] = useState(false)
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false)
const [deleteAppError, setDeleteAppError] = useState<{ message: string, details?: string[] } | null>(null)
const [formData, setFormData] = useState({
name: "",
description: "",
Expand All @@ -53,6 +54,7 @@ export default function ApplicationSettingsPage({
const [isSavingEnv, setIsSavingEnv] = useState(false)
const [confirmDeleteEnvOpen, setConfirmDeleteEnvOpen] = useState(false)
const [selectedEnvForDelete, setSelectedEnvForDelete] = useState<Environment | null>(null)
const [deleteEnvError, setDeleteEnvError] = useState<{ message: string, details?: string[] } | null>(null)

useEffect(() => {
const fetchAppAndEnvs = async () => {
Expand Down Expand Up @@ -153,7 +155,7 @@ export default function ApplicationSettingsPage({
setEnvDialogOpen(false)
} else {
const errData = await res.json().catch(() => ({}))
setEnvFormError(errData.error || "Error al crear el ambiente")
setEnvFormError(errData.message || errData.error || "Error al crear el ambiente")
}
} else if (envDialogMode === "edit" && selectedEnvForEdit) {
const res = await fetch(`/api/applications/${id}/environments/${selectedEnvForEdit.id}`, {
Expand Down Expand Up @@ -181,7 +183,7 @@ export default function ApplicationSettingsPage({
setEnvDialogOpen(false)
} else {
const errData = await res.json().catch(() => ({}))
setEnvFormError(errData.error || "Error al actualizar el ambiente")
setEnvFormError(errData.message || errData.error || "Error al actualizar el ambiente")
}
}
} catch (error) {
Expand All @@ -194,13 +196,17 @@ export default function ApplicationSettingsPage({

const handleOpenDeleteEnv = (env: Environment) => {
setSelectedEnvForDelete(env)
setDeleteEnvError(null)
setConfirmDeleteEnvOpen(true)
}

const handleDeleteEnvConfirm = async () => {
if (!selectedEnvForDelete) return
console.log("INTENTANDO ELIMINAR ENTORNO:", selectedEnvForDelete.name, "CON ID:", selectedEnvForDelete.id)
setIsSavingEnv(true)
setDeleteEnvError(null)
try {
console.log("URL DE FETCH:", `/api/applications/${id}/environments/${selectedEnvForDelete.id}`)
const res = await fetch(`/api/applications/${id}/environments/${selectedEnvForDelete.id}`, {
method: "DELETE",
})
Expand All @@ -209,10 +215,15 @@ export default function ApplicationSettingsPage({
setConfirmDeleteEnvOpen(false)
setSelectedEnvForDelete(null)
} else {
console.error("Failed to delete environment")
const errData = await res.json().catch(() => ({ message: "Error al eliminar el ambiente" }))
setDeleteEnvError({
message: errData.message || errData.error || "Error al eliminar el ambiente",
details: errData.details
})
}
} catch (error) {
console.error("Error deleting environment:", error)
setDeleteEnvError({ message: "Ocurrió un error inesperado al eliminar el ambiente" })
} finally {
setIsSavingEnv(false)
}
Expand Down Expand Up @@ -242,17 +253,23 @@ export default function ApplicationSettingsPage({

const handleDelete = async () => {
setIsSaving(true)
setDeleteAppError(null)
try {
const response = await fetch(`/api/applications/${id}`, {
method: "DELETE",
})
if (response.ok) {
router.push("/dashboard/applications")
} else {
console.error("Failed to delete application")
const errData = await response.json().catch(() => ({ message: "Error al eliminar la aplicación" }))
setDeleteAppError({
message: errData.message || errData.error || "Error al eliminar la aplicación",
details: errData.details
})
}
} catch (error) {
console.error("Error deleting application:", error)
setDeleteAppError({ message: "Ocurrió un error inesperado al eliminar la aplicación" })
} finally {
setIsSaving(false)
}
Expand Down Expand Up @@ -467,7 +484,10 @@ export default function ApplicationSettingsPage({
</div>

{/* Delete Dialog */}
<Dialog open={deleteDialogOpen} onOpenChange={setDeleteDialogOpen}>
<Dialog open={deleteDialogOpen} onOpenChange={(open) => {
setDeleteDialogOpen(open)
if (!open) setDeleteAppError(null)
}}>
<DialogContent>
<DialogHeader>
<DialogTitle>Eliminar Aplicación</DialogTitle>
Expand All @@ -480,10 +500,25 @@ export default function ApplicationSettingsPage({
Esta acción no se puede deshacer.
</DialogDescription>
</DialogHeader>
{deleteAppError && (
<div className="bg-destructive/15 border border-destructive/30 text-destructive text-sm rounded-md p-3 space-y-2 mt-2">
<div className="font-semibold">{deleteAppError.message}</div>
{deleteAppError.details && deleteAppError.details.length > 0 && (
<ul className="list-disc list-inside space-y-1 ml-1">
{deleteAppError.details.map((detail, idx) => (
<li key={idx}>{detail}</li>
))}
</ul>
)}
</div>
)}
<DialogFooter>
<Button
variant="outline"
onClick={() => setDeleteDialogOpen(false)}
onClick={() => {
setDeleteDialogOpen(false)
setDeleteAppError(null)
}}
>
Cancelar
</Button>
Expand Down Expand Up @@ -559,7 +594,10 @@ export default function ApplicationSettingsPage({
</Dialog>

{/* Delete Environment Confirmation Dialog */}
<Dialog open={confirmDeleteEnvOpen} onOpenChange={setConfirmDeleteEnvOpen}>
<Dialog open={confirmDeleteEnvOpen} onOpenChange={(open) => {
setConfirmDeleteEnvOpen(open)
if (!open) setDeleteEnvError(null)
}}>
<DialogContent>
<DialogHeader>
<DialogTitle>Eliminar Ambiente</DialogTitle>
Expand All @@ -569,10 +607,25 @@ export default function ApplicationSettingsPage({
Esta acción es irreversible y eliminará todos los recursos, locks y API keys asociados a este ambiente.
</DialogDescription>
</DialogHeader>
{deleteEnvError && (
<div className="bg-destructive/15 border border-destructive/30 text-destructive text-sm rounded-md p-3 space-y-2 mt-2">
<div className="font-semibold">{deleteEnvError.message}</div>
{deleteEnvError.details && deleteEnvError.details.length > 0 && (
<ul className="list-disc list-inside space-y-1 ml-1">
{deleteEnvError.details.map((detail, idx) => (
<li key={idx}>{detail}</li>
))}
</ul>
)}
</div>
)}
<DialogFooter>
<Button
variant="outline"
onClick={() => setConfirmDeleteEnvOpen(false)}
onClick={() => {
setConfirmDeleteEnvOpen(false)
setDeleteEnvError(null)
}}
disabled={isSavingEnv}
>
Cancelar
Expand Down
36 changes: 30 additions & 6 deletions app/dashboard/applications/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,11 +113,12 @@ export default function ApplicationsPage() {
const [searchQuery, setSearchQuery] = useState("")
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false)
const [appToDelete, setAppToDelete] = useState<Application | null>(null)
const [deleteAppError, setDeleteAppError] = useState<{ message: string, details?: string[] } | null>(null)

useEffect(() => {
const fetchApps = async () => {
try {
const res = await fetch("/api/applications");
const res = await fetch("/api/applications", { cache: "no-store" });
if (res.ok) {
const data = await res.json();
if (data && data.content) {
Expand Down Expand Up @@ -176,20 +177,25 @@ export default function ApplicationsPage() {

const handleDeleteConfirm = async () => {
if (appToDelete) {
setDeleteAppError(null)
try {
const response = await fetch(`/api/applications/${appToDelete.id}`, {
method: "DELETE",
});
if (response.ok) {
setApplications((prev) => prev.filter((app) => app.id !== appToDelete.id))
setDeleteDialogOpen(false)
setAppToDelete(null)
} else {
console.error("Failed to delete application");
const errData = await response.json().catch(() => ({ message: "Error al eliminar la aplicación" }))
setDeleteAppError({
message: errData.message || errData.error || "Error al eliminar la aplicación",
details: errData.details
})
}
} catch (error) {
console.error("Error deleting application:", error);
} finally {
setDeleteDialogOpen(false)
setAppToDelete(null)
setDeleteAppError({ message: "Ocurrió un error inesperado al eliminar la aplicación" })
}
}
}
Expand Down Expand Up @@ -398,7 +404,13 @@ export default function ApplicationsPage() {
)}

{/* Delete Confirmation Dialog */}
<Dialog open={deleteDialogOpen} onOpenChange={setDeleteDialogOpen}>
<Dialog open={deleteDialogOpen} onOpenChange={(open) => {
setDeleteDialogOpen(open)
if (!open) {
setAppToDelete(null)
setDeleteAppError(null)
}
}}>
<DialogContent>
<DialogHeader>
<DialogTitle>Eliminar Aplicación</DialogTitle>
Expand All @@ -411,6 +423,18 @@ export default function ApplicationsPage() {
configuraciones, API keys y datos asociados.
</DialogDescription>
</DialogHeader>
{deleteAppError && (
<div className="bg-destructive/15 border border-destructive/30 text-destructive text-sm rounded-md p-3 space-y-2 mt-2">
<div className="font-semibold">{deleteAppError.message}</div>
{deleteAppError.details && deleteAppError.details.length > 0 && (
<ul className="list-disc list-inside space-y-1 ml-1">
{deleteAppError.details.map((detail, idx) => (
<li key={idx}>{detail}</li>
))}
</ul>
)}
</div>
)}
<DialogFooter>
<Button
variant="outline"
Expand Down
1 change: 1 addition & 0 deletions lib/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export async function fetchBackend(path: string, options: RequestInit = {}) {
}

const response = await fetch(url, {
cache: "no-store",
...options,
headers: mergedHeaders,
});
Expand Down
2 changes: 1 addition & 1 deletion next-env.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
import "./.next/types/routes.d.ts";
import "./.next/dev/types/routes.d.ts";

// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.