feat: implemented delete application endpoint and dashboard action

This commit is contained in:
Tiago Yamamoto 2025-12-26 01:29:31 -03:00
parent 7b76b62490
commit 3fa875ed98
6 changed files with 74 additions and 6 deletions

View file

@ -477,7 +477,7 @@ func extractClientIP(r *http.Request) *string {
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Security BearerAuth // @Security BearerAuth
// @Success 200 {array} models.Notification // @Success 200 {array} object
// @Failure 500 {string} string "Internal Server Error" // @Failure 500 {string} string "Internal Server Error"
// @Router /api/v1/notifications [get] // @Router /api/v1/notifications [get]
func (h *CoreHandlers) ListNotifications(w http.ResponseWriter, r *http.Request) { func (h *CoreHandlers) ListNotifications(w http.ResponseWriter, r *http.Request) {
@ -566,7 +566,7 @@ func (h *CoreHandlers) MarkAllNotificationsAsRead(w http.ResponseWriter, r *http
// @Produce json // @Produce json
// @Security BearerAuth // @Security BearerAuth
// @Param ticket body object true "Ticket Details" // @Param ticket body object true "Ticket Details"
// @Success 201 {object} models.Ticket // @Success 201 {object} object
// @Failure 400 {string} string "Invalid Request" // @Failure 400 {string} string "Invalid Request"
// @Failure 500 {string} string "Internal Server Error" // @Failure 500 {string} string "Internal Server Error"
// @Router /api/v1/support/tickets [post] // @Router /api/v1/support/tickets [post]
@ -607,7 +607,7 @@ func (h *CoreHandlers) CreateTicket(w http.ResponseWriter, r *http.Request) {
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Security BearerAuth // @Security BearerAuth
// @Success 200 {array} models.Ticket // @Success 200 {array} object
// @Failure 500 {string} string "Internal Server Error" // @Failure 500 {string} string "Internal Server Error"
// @Router /api/v1/support/tickets [get] // @Router /api/v1/support/tickets [get]
func (h *CoreHandlers) ListTickets(w http.ResponseWriter, r *http.Request) { func (h *CoreHandlers) ListTickets(w http.ResponseWriter, r *http.Request) {
@ -680,7 +680,7 @@ func (h *CoreHandlers) GetTicket(w http.ResponseWriter, r *http.Request) {
// @Security BearerAuth // @Security BearerAuth
// @Param id path string true "Ticket ID" // @Param id path string true "Ticket ID"
// @Param message body object true "Message" // @Param message body object true "Message"
// @Success 201 {object} models.TicketMessage // @Success 201 {object} object
// @Failure 400 {string} string "Invalid Request" // @Failure 400 {string} string "Invalid Request"
// @Failure 500 {string} string "Internal Server Error" // @Failure 500 {string} string "Internal Server Error"
// @Router /api/v1/support/tickets/{id}/messages [post] // @Router /api/v1/support/tickets/{id}/messages [post]

View file

@ -139,3 +139,23 @@ func (h *ApplicationHandler) UpdateApplicationStatus(w http.ResponseWriter, r *h
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(app) json.NewEncoder(w).Encode(app)
} }
// DeleteApplication removes an application
// @Summary Delete Application
// @Description Remove an application by ID
// @Tags Applications
// @Accept json
// @Produce json
// @Param id path string true "Application ID"
// @Success 204 {object} nil
// @Failure 400 {string} string "Bad Request"
// @Failure 500 {string} string "Internal Server Error"
// @Router /api/v1/applications/{id} [delete]
func (h *ApplicationHandler) DeleteApplication(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
if err := h.Service.DeleteApplication(id); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusNoContent)
}

View file

@ -200,6 +200,7 @@ func NewRouter() http.Handler {
mux.HandleFunc("GET /api/v1/applications", applicationHandler.GetApplications) mux.HandleFunc("GET /api/v1/applications", applicationHandler.GetApplications)
mux.HandleFunc("GET /api/v1/applications/{id}", applicationHandler.GetApplicationByID) mux.HandleFunc("GET /api/v1/applications/{id}", applicationHandler.GetApplicationByID)
mux.HandleFunc("PUT /api/v1/applications/{id}/status", applicationHandler.UpdateApplicationStatus) mux.HandleFunc("PUT /api/v1/applications/{id}/status", applicationHandler.UpdateApplicationStatus)
mux.HandleFunc("DELETE /api/v1/applications/{id}", applicationHandler.DeleteApplication)
// --- STORAGE ROUTES --- // --- STORAGE ROUTES ---
// Initialize S3 Storage (optional - graceful degradation if not configured) // Initialize S3 Storage (optional - graceful degradation if not configured)

View file

@ -141,3 +141,9 @@ func (s *ApplicationService) GetApplicationsByCompany(companyID string) ([]model
} }
return apps, nil return apps, nil
} }
func (s *ApplicationService) DeleteApplication(id string) error {
query := `DELETE FROM applications WHERE id = $1`
_, err := s.DB.Exec(query, id)
return err
}

View file

@ -33,7 +33,10 @@ import {
X, X,
Clock, Clock,
Star, Star,
Trash,
} from "lucide-react" } from "lucide-react"
import { applicationsApi } from "@/lib/api"
import { toast } from "sonner"
// Mock data - in production this would come from API // Mock data - in production this would come from API
const mockApplications = [ const mockApplications = [
@ -108,6 +111,21 @@ export default function ApplicationsPage() {
const [searchTerm, setSearchTerm] = useState("") const [searchTerm, setSearchTerm] = useState("")
const [selectedApp, setSelectedApp] = useState<typeof mockApplications[0] | null>(null) const [selectedApp, setSelectedApp] = useState<typeof mockApplications[0] | null>(null)
const handleDelete = async (id: string, e?: React.MouseEvent) => {
e?.stopPropagation()
if (!confirm("Are you sure you want to delete this application?")) return
try {
await applicationsApi.delete(id)
setApplications(applications.filter(a => a.id !== id))
toast.success("Application deleted")
if (selectedApp?.id === id) setSelectedApp(null)
} catch (error) {
console.error("Delete error:", error)
toast.error("Failed to delete application")
}
}
const filteredApplications = applications.filter((app) => { const filteredApplications = applications.filter((app) => {
const matchesStatus = statusFilter === "all" || app.status === statusFilter const matchesStatus = statusFilter === "all" || app.status === statusFilter
const matchesSearch = const matchesSearch =
@ -265,6 +283,14 @@ export default function ApplicationsPage() {
<MessageSquare className="h-4 w-4 mr-2" /> <MessageSquare className="h-4 w-4 mr-2" />
Message Message
</Button> </Button>
<Button
variant="destructive"
size="sm"
className="flex-1 sm:flex-none"
onClick={(e) => handleDelete(app.id, e)}
>
<Trash className="h-4 w-4" />
</Button>
</div> </div>
</div> </div>
</CardContent> </CardContent>
@ -338,11 +364,19 @@ export default function ApplicationsPage() {
<MessageSquare className="h-4 w-4 mr-2" /> <MessageSquare className="h-4 w-4 mr-2" />
Contact Contact
</Button> </Button>
<Button
className="flex-none"
variant="destructive"
onClick={() => handleDelete(selectedApp.id)}
>
<Trash className="h-4 w-4" />
</Button>
</div> </div>
</CardContent> </CardContent>
</Card> </Card>
</div> </div>
)} )
</div> }
</div >
) )
} }

View file

@ -307,6 +307,8 @@ export const adminCompaniesApi = {
} }
}; };
// --- Jobs API (Public/Candidate) --- // --- Jobs API (Public/Candidate) ---
export interface CreateJobPayload { export interface CreateJobPayload {
companyId: string; companyId: string;
@ -396,6 +398,11 @@ export const applicationsApi = {
if (params.companyId) query.append("companyId", params.companyId); if (params.companyId) query.append("companyId", params.companyId);
return apiRequest<any[]>(`/api/v1/applications?${query.toString()}`); return apiRequest<any[]>(`/api/v1/applications?${query.toString()}`);
}, },
delete: (id: string) => {
return apiRequest<void>(`/api/v1/applications/${id}`, {
method: "DELETE"
})
},
}; };
// --- Helper Functions --- // --- Helper Functions ---