feature(ui): permettre la modification de fichiers par UI #222

Merged
vlbeaudoin merged 1 commit from vlbeaudoin/feature/modifier-documents into main 2024-10-31 19:19:47 -04:00 AGit
9 changed files with 315 additions and 1 deletions

View file

@ -175,6 +175,22 @@ func (a *API) ListBuckets() (response ListBucketsResponse, err error) {
return response, a.Voki.Unmarshal(http.MethodGet, "/v1/bucket", nil, true, &response) return response, a.Voki.Unmarshal(http.MethodGet, "/v1/bucket", nil, true, &response)
} }
func (a *API) ReadBucket(bucket string) (response ReadBucketResponse, err error) {
return response, a.Voki.Unmarshal(http.MethodGet, fmt.Sprintf("/v1/bucket/%s", bucket), nil, true, &response)
}
func (a *API) UpdateDocumentKey(bucket, document, newKey string) (response UpdateDocumentKeyResponse, err error) {
var buf bytes.Buffer
if err := json.NewEncoder(&buf).Encode(newKey); err != nil {
return response, fmt.Errorf("handler: %s", err)
}
return response, a.Voki.Unmarshal(http.MethodPut, fmt.Sprintf("/v1/bucket/%s/%s/key", bucket, document), &buf, true, &response)
}
func (a *API) DeleteDocument(bucket, document string) (response DeleteDocumentResponse, err error) {
return response, a.Voki.Unmarshal(http.MethodDelete, fmt.Sprintf("/v1/bucket/%s/%s", bucket, document), nil, true, &response)
}
func (a *API) Seed() (response ExecuteSeedResponse, err error) { func (a *API) Seed() (response ExecuteSeedResponse, err error) {
request, err := NewV1SeedPOST() request, err := NewV1SeedPOST()
if err != nil { if err != nil {

View file

@ -4,6 +4,7 @@ import (
"context" "context"
"fmt" "fmt"
"io" "io"
"log"
"net/http" "net/http"
"net/url" "net/url"
"sort" "sort"
@ -686,6 +687,151 @@ func HandlePageProcesVerbaux() echo.HandlerFunc {
} }
} }
func HandleAdminDocumentsEditDelete(api *API) echo.HandlerFunc {
return func(c echo.Context) error {
switch c.Request().Method {
case http.MethodGet:
var data struct {
Error string
Bucket string
Document string
}
data.Bucket = c.Param("bucket")
data.Document = c.Param("document")
return c.Render(http.StatusOK, "dialog-document-delete", data)
case http.MethodDelete:
var data struct {
Error string
Bucket string
Document string
DeleteDocumentResponse DeleteDocumentResponse
}
if api == nil {
errMsg := "Error: HandleAdminDocumentsEditDelete called with nil *API"
log.Println(errMsg)
data.Error = errMsg
return c.String(http.StatusOK, data.Error)
}
data.Bucket = c.Param("bucket")
data.Document = c.Param("document")
deleteDocumentResponse, err := api.DeleteDocument(data.Bucket, data.Document)
if err != nil {
log.Println(err)
data.Error = err.Error()
return c.Render(http.StatusOK, "dialog-error", data)
}
data.DeleteDocumentResponse = deleteDocumentResponse
return c.Render(http.StatusOK, "dialog-document-delete-result", data)
default:
return c.String(http.StatusOK, "unsupported method")
}
}
}
func HandleAdminDocumentsEditKey(api *API) echo.HandlerFunc {
return func(c echo.Context) error {
switch c.Request().Method {
case http.MethodGet:
var data struct {
Error string
Bucket string
Document string
}
data.Bucket = c.Param("bucket")
data.Document = c.Param("document")
return c.Render(http.StatusOK, "dialog-document-rename", data)
case http.MethodPut:
var data struct {
Error string
Bucket string
Document string
UpdateDocumentKeyResponse UpdateDocumentKeyResponse
}
if api == nil {
errMsg := "Error: HandleAdminDocumentsEdit called with nil *API"
log.Println(errMsg)
data.Error = errMsg
return c.String(http.StatusOK, data.Error)
}
data.Bucket = c.Param("bucket")
data.Document = c.Param("document")
newKey := c.FormValue("rename-document-form-newkey")
updateDocumentKeyResponse, err := api.UpdateDocumentKey(data.Bucket, data.Document, newKey)
if err != nil {
log.Println(err)
data.Error = err.Error()
return c.Render(http.StatusOK, "dialog-error", data)
}
data.UpdateDocumentKeyResponse = updateDocumentKeyResponse
return c.Render(http.StatusOK, "dialog-document-rename-result", data)
default:
return c.String(http.StatusOK, "unsupported method")
}
}
}
func HandleNothing() echo.HandlerFunc {
return func(c echo.Context) error {
return c.String(http.StatusOK, "")
}
}
func HandleAdminDocumentsEdit(api *API) echo.HandlerFunc {
return func(c echo.Context) error {
type DataBucket struct {
ID string
DisplayName string
DocumentKeys []string
}
var data struct {
Error string
Buckets []DataBucket
}
if api == nil {
errMsg := "Error: HandleAdminDocumentsEdit called with nil *API"
log.Println(errMsg)
data.Error = errMsg
return c.Render(http.StatusOK, "admin-edit-html", data)
}
listBucketsResponse, err := api.ListBuckets()
if err != nil {
log.Println(err)
data.Error = err.Error()
return c.Render(http.StatusOK, "admin-edit-html", data)
}
for name, displayName := range listBucketsResponse.Data.Buckets {
readBucketResponse, err := api.ReadBucket(name)
if err != nil {
log.Println(err)
}
dataBucket := DataBucket{
ID: name,
DisplayName: displayName,
DocumentKeys: readBucketResponse.Data.Keys,
}
data.Buckets = append(data.Buckets, dataBucket)
}
return c.Render(http.StatusOK, "admin-edit-html", data)
}
}
func (h *WebHandler) HandleAdminDocumentsUpload(c echo.Context) error { func (h *WebHandler) HandleAdminDocumentsUpload(c echo.Context) error {
var response HandleAdminDocumentsUploadResponse var response HandleAdminDocumentsUploadResponse

View file

@ -152,6 +152,8 @@ func RunServer(cfg Config) {
e.GET("/", HandleIndex) e.GET("/", HandleIndex)
e.GET("/nothing", HandleNothing())
//e.GET("/a-propos", HandleAPropos) //e.GET("/a-propos", HandleAPropos)
//e.GET("/actualite", HandleActualite) //e.GET("/actualite", HandleActualite)
@ -180,6 +182,12 @@ func RunServer(cfg Config) {
groupAdmin.POST("/documents/upload", webHandler.HandleAdminDocumentsUploadPOST) groupAdmin.POST("/documents/upload", webHandler.HandleAdminDocumentsUploadPOST)
groupAdmin.GET("/documents/edit", HandleAdminDocumentsEdit(webHandler.ApiClient))
groupAdmin.GET("/documents/edit/:bucket/:document/delete", HandleAdminDocumentsEditDelete(webHandler.ApiClient))
groupAdmin.DELETE("/documents/edit/:bucket/:document", HandleAdminDocumentsEditDelete(webHandler.ApiClient))
groupAdmin.GET("/documents/edit/:bucket/:document/key", HandleAdminDocumentsEditKey(webHandler.ApiClient))
groupAdmin.PUT("/documents/edit/:bucket/:document/key", HandleAdminDocumentsEditKey(webHandler.ApiClient))
e.Logger.Fatal(e.Start( e.Logger.Fatal(e.Start(
fmt.Sprintf(":%d", cfg.Server.Port))) fmt.Sprintf(":%d", cfg.Server.Port)))
} }

55
ui/admin-edit.html Normal file
View file

@ -0,0 +1,55 @@
{{ define "admin-edit-html" }}
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8">
<title>AGECEM</title>
{{ template "general-html" }}
<link rel="stylesheet" href="/public/css/admin-edit.css">
<script src="https://unpkg.com/htmx.org@2.0.3"></script>
</head>
<body>
{{ template "header-html" }}
{{ if .Error }}
<b>{{ .Error }}</b>
{{ end }}
<article id="moduleAdminEdit">
<h2>Modifier les documents</h2>
{{ range .Buckets }}
{{ $bucketID := .ID }}
<h3>{{ .DisplayName }} ({{ .ID }})</h3>
{{ if not .DocumentKeys }}
<p><i>Aucun document de type {{ .DisplayName }} enregistré, voir le <a href="/admin">panneau d'administration</a> pour en ajouter.</i></p>
{{ else }}
<table>
<tr><th></th><th>Renommer</th><th>Effacer</th></tr>
{{ range .DocumentKeys }}
<tr>
<td>{{ . }}</td>
<td><a
hx-get="/admin/documents/edit/{{$bucketID}}/{{.}}/key"
hx-target="#app-dialog"
hx-on:click="document.getElementById('app-dialog').style.display = 'block';">
<button>🖉</button>
</a></td>
<td><a
hx-get="/admin/documents/edit/{{$bucketID}}/{{.}}/delete"
hx-target="#app-dialog"
hx-on:click="document.getElementById('app-dialog').style.display = 'block';">
<button>🗑</button>
</a></td>
</tr>
{{ end }}
</table>
{{ end }}
{{ end }}
<div id="app-dialog" style="display: none;"> </div>
</article>
{{ template "footer-html" }}
</body>
</html>
{{ end }}

View file

@ -15,6 +15,7 @@
<h1 class="heading1">Admin</h1> <h1 class="heading1">Admin</h1>
<div class="adminOptions"> <div class="adminOptions">
<button class="adminOption" onclick="location.href = '/admin/documents/upload'">Ajout de documents</a> <button class="adminOption" onclick="location.href = '/admin/documents/upload'">Ajout de documents</a>
<button class="adminOption" onclick="location.href = '/admin/documents/edit'">Modification des documents</a>
</div> </div>
</div> </div>
{{ template "footer-html" }} {{ template "footer-html" }}

View file

@ -0,0 +1,23 @@
{{ define "dialog-document-delete" }}
{{ if .Error }}
{{ .Error }}
{{ else }}
<form
action=""
id="delete-document-form"
hx-delete="/admin/documents/edit/{{.Bucket}}/{{.Document}}"
hx-target="#app-dialog">
<label>
Effacer fichier <code>{{ .Bucket }}</code> / <code>{{ .Document }}</code>?
</label>
<br>
<input type="submit" id="delete-document-apply" value="Confirmer"/>
{{ template "dialog-button-clear" }}
</form>
{{ end }}
{{ end }}
{{ define "dialog-document-delete-result" }}
{{ . }}
{{ template "dialog-button-clear" "ok" }}
{{ end }}

View file

@ -0,0 +1,32 @@
{{ define "dialog-document-rename" }}
{{ if .Error }}
{{ .Error }}
{{ else }}
<form
action=""
id="rename-document-form"
hx-put="/admin/documents/edit/{{.Bucket}}/{{.Document}}/key"
hx-target="#app-dialog">
<label for="rename-document-form-newkey">
Nouveau nom de fichier pour <code>{{.Bucket}}</code> / <code>{{.Document}}</code>:
</label>
<input
type="text"
id="rename-document-form-newkey"
name="rename-document-form-newkey"
required
selected/>
<br>
<input
type="submit"
id="rename-document-form-apply"
value="Confirmer"/>
{{ template "dialog-button-clear" }}
</form>
{{ end }}
{{ end }}
{{ define "dialog-document-rename-result" }}
{{ . }}
{{ template "dialog-button-clear" "ok" }}
{{ end }}

13
ui/dialog.html Normal file
View file

@ -0,0 +1,13 @@
{{ define "dialog-button-clear" }}
<button
hx-get="/nothing"
hx-target="#app-dialog"
hx-on:click="document.getElementById('app-dialog').style.display = 'none';">
{{ if not . }}Annuler{{ else }}{{ . }}{{ end }}
</button>
{{ end }}
{{ define "dialog-error" }}
<p>Error: {{ .Error }}</p>
{{ template "dialog-button-clear" }}
{{ end }}

View file

@ -0,0 +1,20 @@
table {
border: 1px solid;
border-collapse: collapse;
}
td, th {
border: 1px solid;
}
#app-dialog {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: white;
border: 1px solid #ccc;
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.3);
padding: 20px;
z-index: 555;
}