Model clone button on model list

Each model row now has a Clone button that duplicates the model with all
its ports, flags, and images, appending '(copy)' to the name. Saves
re-creating 48 ports when making a sibling switch model.
master
Joca 2026-06-09 20:15:18 -03:00
parent 17ae561828
commit ef063100f2
Signed by: jocadbz
GPG Key ID: B1836DCE2F50BDF7
3 changed files with 40 additions and 0 deletions

View File

@ -163,6 +163,42 @@ func (h *Handlers) ModelDelete(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/models", http.StatusSeeOther) http.Redirect(w, r, "/models", http.StatusSeeOther)
} }
func (h *Handlers) ModelClone(w http.ResponseWriter, r *http.Request) {
idStr := r.PathValue("id")
id, _ := strconv.ParseInt(idStr, 10, 64)
orig, err := h.Store.ModelGetByID(id)
if err != nil || orig == nil {
h.renderModelListError(w, "Model not found.")
return
}
clone := &models.DeviceModel{
Name: orig.Name + " (copy)",
Manufacturer: orig.Manufacturer,
IsRackMountable: orig.IsRackMountable,
HeightUnits: orig.HeightUnits,
FrontImage: orig.FrontImage,
BackImage: orig.BackImage,
IsPatchPanel: orig.IsPatchPanel,
IsWallSocket: orig.IsWallSocket,
IsPowerStrip: orig.IsPowerStrip,
Ports: orig.Ports,
}
_, err = h.Store.ModelCreate(clone, "uploads")
if err != nil {
msg := err.Error()
if strings.Contains(strings.ToLower(msg), "unique") {
msg = "A model named '" + clone.Name + "' already exists, onii-chan."
}
h.renderModelListError(w, msg)
return
}
http.Redirect(w, r, "/models", http.StatusSeeOther)
}
func (h *Handlers) renderModelListError(w http.ResponseWriter, msg string) { func (h *Handlers) renderModelListError(w http.ResponseWriter, msg string) {
list, _ := h.Store.ModelGetAll() list, _ := h.Store.ModelGetAll()
if list == nil { if list == nil {

View File

@ -45,6 +45,7 @@ func main() {
mux.HandleFunc("GET /models/{id}/edit", h.ModelEditForm) mux.HandleFunc("GET /models/{id}/edit", h.ModelEditForm)
mux.HandleFunc("POST /models/{id}/update", h.ModelUpdate) mux.HandleFunc("POST /models/{id}/update", h.ModelUpdate)
mux.HandleFunc("POST /models/{id}/delete", h.ModelDelete) mux.HandleFunc("POST /models/{id}/delete", h.ModelDelete)
mux.HandleFunc("POST /models/{id}/clone", h.ModelClone)
mux.HandleFunc("GET /connections/{portId}", h.ConnectionModal) mux.HandleFunc("GET /connections/{portId}", h.ConnectionModal)
mux.HandleFunc("GET /connections/connect-form", h.ConnectionConnectForm) mux.HandleFunc("GET /connections/connect-form", h.ConnectionConnectForm)
mux.HandleFunc("POST /connections/create", h.ConnectionCreate) mux.HandleFunc("POST /connections/create", h.ConnectionCreate)

View File

@ -20,6 +20,9 @@
<td>{{if .IsWallSocket}}yes{{end}}</td> <td>{{if .IsWallSocket}}yes{{end}}</td>
<td> <td>
<a href="/models/{{.ID}}/edit" class="btn-sm">Edit</a> <a href="/models/{{.ID}}/edit" class="btn-sm">Edit</a>
<form method="POST" action="/models/{{.ID}}/clone" style="display:inline">
<button class="btn-sm">Clone</button>
</form>
<form method="POST" action="/models/{{.ID}}/delete" style="display:inline" onsubmit="return confirm('Delete model?')"> <form method="POST" action="/models/{{.ID}}/delete" style="display:inline" onsubmit="return confirm('Delete model?')">
<button class="btn-sm btn-danger">Delete</button> <button class="btn-sm btn-danger">Delete</button>
</form> </form>