lens @ 249ee195ce52ee4a4defeb67a33ef353919d3a11

feat: Add user list UI

Fill user settings UI with actual data.
  1diff --git a/README.md b/README.md
  2index 6103dbfc3432ba7615a19ab7d4026389bd854e9b..9ff9bfd53f0a3058505d4b70861d40120fb3537d 100644
  3--- a/README.md
  4+++ b/README.md
  5@@ -13,3 +13,5 @@ * Alpine package and demo site
  6 * Single image viewer and show exif info (not sure how yet)
  7 * User base root folder 
  8 * Albuns
  9+* Testing. Since I still on initial iteration phases I'm not adding as many
 10+  testing as I'd like to. Once I set on most of the design I'll add testing.
 11diff --git a/cmd/server/main.go b/cmd/server/main.go
 12index 4ca39de019df146d6b4348036a23abaa1087d090..d7c2fd64ce0eaf97d3afc4e3d3d6337ac5995109 100644
 13--- a/cmd/server/main.go
 14+++ b/cmd/server/main.go
 15@@ -103,7 +103,7 @@ 	// view
 16 	for _, v := range []view.View{
 17 		view.NewAuthView(userController),
 18 		view.NewFileSystemView(*fileSystemController, settingsRepository),
 19-		view.NewSettingsView(settingsRepository),
 20+		view.NewSettingsView(settingsRepository, userRepository),
 21 		view.NewMediaView(mediaRepository),
 22 	} {
 23 		v.SetMyselfIn(extRouter)
 24diff --git a/pkg/components/auth/controller.go b/pkg/components/auth/controller.go
 25index 4da607129965146d0a5cef01599d464e421f5875..a81a1c0f802acc5a4c90bcc400d2d9f913c73b9b 100644
 26--- a/pkg/components/auth/controller.go
 27+++ b/pkg/components/auth/controller.go
 28@@ -41,17 +41,3 @@ 		Username: string(username),
 29 	}
 30 	return ext.WriteToken(token, c.key)
 31 }
 32-
 33-func (c *Controller) Register(ctx context.Context, username, password []byte) error {
 34-	hash, err := bcrypt.GenerateFromPassword(password, bcrypt.MinCost)
 35-	if err != nil {
 36-		return err
 37-	}
 38-
 39-	_, err = c.repository.Create(ctx, &CreateUser{
 40-		Username: string(username),
 41-		Password: hash,
 42-	})
 43-
 44-	return err
 45-}
 46diff --git a/pkg/components/auth/model.go b/pkg/components/auth/model.go
 47index e46ef4979fc10a82e1822ab6a692c19774093525..dd6ce5036b91127e13e37c4e57b7baa13b486a1a 100644
 48--- a/pkg/components/auth/model.go
 49+++ b/pkg/components/auth/model.go
 50@@ -3,30 +3,8 @@
 51 import "context"
 52 
 53 type (
 54-	// TODO: move to user later
 55-	User struct {
 56-		ID       uint
 57-		Username string
 58-		Name     string
 59-	}
 60-
 61-	// TODO: move to user later
 62-	UpdateUser struct {
 63-		Username string
 64-		Name     string
 65-	}
 66-
 67-	// TODO: move to user later
 68-	CreateUser struct {
 69-		Username string
 70-		Name     string
 71-		Password []byte
 72-	}
 73-
 74 	Repository interface {
 75 		GetIDByUsername(ctx context.Context, username string) (uint, error)
 76 		GetPassword(ctx context.Context, id uint) ([]byte, error)
 77-		// TODO: move to user later
 78-		Create(ctx context.Context, createUser *CreateUser) (uint, error)
 79 	}
 80 )
 81diff --git a/pkg/components/user/controller.go b/pkg/components/user/controller.go
 82new file mode 100644
 83index 0000000000000000000000000000000000000000..a00006b65468ecad731002a1e2c54884759656ed
 84--- /dev/null
 85+++ b/pkg/components/user/controller.go
 86@@ -0,0 +1 @@
 87+package user
 88diff --git a/pkg/components/user/model.go b/pkg/components/user/model.go
 89new file mode 100644
 90index 0000000000000000000000000000000000000000..f957c39a95fb49179c319674a6c31827bebb695c
 91--- /dev/null
 92+++ b/pkg/components/user/model.go
 93@@ -0,0 +1,33 @@
 94+package user
 95+
 96+import "context"
 97+
 98+type (
 99+	User struct {
100+		ID       uint
101+		Username string
102+		Name     string
103+		IsAdmin  bool
104+		Path     string
105+	}
106+
107+	UpdateUser struct {
108+		Username string
109+		Name     string
110+		Password *string
111+	}
112+
113+	CreateUser struct {
114+		Username string
115+		Name     string
116+		Password string
117+		IsAdmin  bool
118+		Path     string
119+	}
120+
121+	Repository interface {
122+		List(ctx context.Context) ([]*User, error)
123+		Create(ctx context.Context, createUser *CreateUser) error
124+		Update(ctx context.Context, id uint, updateUser *UpdateUser) error
125+	}
126+)
127diff --git a/pkg/database/sql/user.go b/pkg/database/sql/user.go
128index d449b05078914829508c79dd8da8583b7f66cbc9..2d74162713c10e1ed292a1dc116404e862f77bd3 100644
129--- a/pkg/database/sql/user.go
130+++ b/pkg/database/sql/user.go
131@@ -7,7 +7,7 @@ 	"golang.org/x/crypto/bcrypt"
132 	"gorm.io/gorm"
133 
134 	"git.sr.ht/~gabrielgio/img/pkg/components/auth"
135-	user "git.sr.ht/~gabrielgio/img/pkg/components/auth"
136+	"git.sr.ht/~gabrielgio/img/pkg/components/user"
137 )
138 
139 type (
140@@ -16,6 +16,8 @@ 		gorm.Model
141 		Username string
142 		Name     string
143 		Password string
144+		IsAdmin  bool
145+		Path     string
146 	}
147 
148 	Users []*User
149@@ -26,6 +28,7 @@ 	}
150 )
151 
152 var _ auth.Repository = &UserRepository{}
153+var _ user.Repository = &UserRepository{}
154 
155 func NewUserRepository(db *gorm.DB) *UserRepository {
156 	return &UserRepository{
157@@ -38,6 +41,8 @@ 	return &user.User{
158 		ID:       self.Model.ID,
159 		Name:     self.Name,
160 		Username: self.Username,
161+		Path:     self.Path,
162+		IsAdmin:  self.IsAdmin,
163 	}
164 }
165 
166@@ -63,6 +68,8 @@ 	if !exists {
167 		hash, _ := bcrypt.GenerateFromPassword([]byte("admin"), bcrypt.MinCost)
168 		self.db.Save(&User{
169 			Username: "admin",
170+			Path:     "/",
171+			IsAdmin:  true,
172 			Password: string(hash),
173 		})
174 	}
175@@ -130,7 +137,7 @@
176 	return userPassword.Password, nil
177 }
178 
179-func (self *UserRepository) Create(ctx context.Context, createUser *user.CreateUser) (uint, error) {
180+func (self *UserRepository) Create(ctx context.Context, createUser *user.CreateUser) error {
181 	user := &User{
182 		Username: createUser.Username,
183 		Name:     createUser.Name,
184@@ -141,10 +148,10 @@ 	result := self.db.
185 		WithContext(ctx).
186 		Create(user)
187 	if result.Error != nil {
188-		return 0, result.Error
189+		return result.Error
190 	}
191 
192-	return user.Model.ID, nil
193+	return nil
194 }
195 
196 func (self *UserRepository) Update(ctx context.Context, id uint, update *user.UpdateUser) error {
197diff --git a/pkg/view/auth.go b/pkg/view/auth.go
198index 5c83eba0225998cda97795f1747f3d73894e9f4d..d44424d64290d5877761bcc1229b9244551ca08b 100644
199--- a/pkg/view/auth.go
200+++ b/pkg/view/auth.go
201@@ -64,23 +64,6 @@ 	}
202 	return nil
203 }
204 
205-func (v *AuthView) RegisterView(ctx *fasthttp.RequestCtx) error {
206-	return img.Render[interface{}](ctx, "register.html", nil)
207-}
208-
209-func (v *AuthView) Register(ctx *fasthttp.RequestCtx) error {
210-	username := ctx.FormValue("username")
211-	password := ctx.FormValue("password")
212-
213-	err := v.userController.Register(ctx, username, password)
214-	if err != nil {
215-		return err
216-	}
217-
218-	ctx.Redirect("/login", 307)
219-	return nil
220-}
221-
222 func Index(ctx *fasthttp.RequestCtx) {
223 	ctx.Redirect("/login", 307)
224 }
225@@ -88,9 +71,6 @@
226 func (v *AuthView) SetMyselfIn(r *ext.Router) {
227 	r.GET("/login", v.LoginView)
228 	r.POST("/login", v.Login)
229-
230-	r.GET("/register", v.RegisterView)
231-	r.POST("/register", v.Register)
232 
233 	r.GET("/logout", v.Logout)
234 	r.POST("/logout", v.Logout)
235diff --git a/pkg/view/settings.go b/pkg/view/settings.go
236index 746dee4df99001f92e4b6703fee352ca4cb76998..e5acb1bbc57cfcb0325646114b7e95342e21c53a 100644
237--- a/pkg/view/settings.go
238+++ b/pkg/view/settings.go
239@@ -5,28 +5,50 @@ 	"github.com/valyala/fasthttp"
240 
241 	"git.sr.ht/~gabrielgio/img"
242 	"git.sr.ht/~gabrielgio/img/pkg/components/settings"
243+	"git.sr.ht/~gabrielgio/img/pkg/components/user"
244 	"git.sr.ht/~gabrielgio/img/pkg/ext"
245 )
246 
247-type SettingsView struct {
248-	// there is not need to create a controller for this
249-	repository settings.Repository
250-}
251+type (
252+	SettingsView struct {
253+		// there is not need to create a controller for this
254+		settingsRepository settings.Repository
255+		userRepository     user.Repository
256+	}
257+
258+	SettingsPage struct {
259+		Settings *settings.Settings
260+		Users    []*user.User
261+	}
262+)
263 
264-func NewSettingsView(respository settings.Repository) *SettingsView {
265+func NewSettingsView(
266+	settingsRespository settings.Repository,
267+	userRepository user.Repository,
268+) *SettingsView {
269 	return &SettingsView{
270-		repository: respository,
271+		settingsRepository: settingsRespository,
272+		userRepository:     userRepository,
273 	}
274 }
275 
276 func (self *SettingsView) Index(ctx *fasthttp.RequestCtx) error {
277-	s, err := self.repository.Load(ctx)
278+	s, err := self.settingsRepository.Load(ctx)
279+	if err != nil {
280+		return err
281+	}
282+
283+	users, err := self.userRepository.List(ctx)
284 	if err != nil {
285 		return err
286 	}
287-	return img.Render(ctx, "settings.html", &img.HTMLView[*settings.Settings]{
288+
289+	return img.Render(ctx, "settings.html", &img.HTMLView[*SettingsPage]{
290 		Title: "Settings",
291-		Data:  s,
292+		Data: &SettingsPage{
293+			Settings: s,
294+			Users:    users,
295+		},
296 	})
297 }
298 
299@@ -36,7 +58,7 @@ 		showMode  = string(ctx.FormValue("showMode")) == "on"
300 		showOwner = string(ctx.FormValue("showOwner")) == "on"
301 	)
302 
303-	err := self.repository.Save(ctx, &settings.Settings{
304+	err := self.settingsRepository.Save(ctx, &settings.Settings{
305 		ShowMode:  showMode,
306 		ShowOwner: showOwner,
307 	})
308diff --git a/scss/main.scss b/scss/main.scss
309index 033315f51b4c6b75ed6da68eb9d5faa25656409b..7028622f93c495f755a63365a8093ba4cc8c1264 100644
310--- a/scss/main.scss
311+++ b/scss/main.scss
312@@ -34,15 +34,20 @@
313 .input, .button{
314     border-radius: 0;
315 }
316-
317-.file-row {
318+.wide-column {
319     width: 100%;
320+}
321+.is-mono {
322     font-family: monospace;
323 }
324 
325 nav {
326     border-bottom: 1px solid;
327     margin: auto;
328+}
329+
330+a.is-danger {
331+    color: $danger
332 }
333 
334 .container {
335diff --git a/templates/fs.html b/templates/fs.html
336index 608289d971d4f83e1c9b5155f63f6059084b5b85..a44d78f441b53a2e1229120696a4c081908679dd 100644
337--- a/templates/fs.html
338+++ b/templates/fs.html
339@@ -11,7 +11,7 @@       </div>
340   </div>
341   {{range .Data.Page.Files}}
342   <div class="panel-block">
343-      <div class="columns file-row is-gapless is-mobile">
344+      <div class="columns wide-column is-mono is-gapless is-mobile">
345           <div class="column">
346               {{if $.Data.ShowMode}}{{.Info.Mode}}&emsp;{{end}}
347               {{if $.Data.ShowOwner}}{{.Info.Sys.Gid}}:{{.Info.Sys.Uid}}&emsp;{{end}}
348diff --git a/templates/settings.html b/templates/settings.html
349index 2aa1a80ea944113afeac78ecc08cac704956187d..8c08773338919637ec0c3cb18e2b050f22945a66 100644
350--- a/templates/settings.html
351+++ b/templates/settings.html
352@@ -7,7 +7,7 @@         <form action="/settings/", method="post">
353             <div class="field">
354                 <div class="control">
355                     <label class="checkbox">
356-                        <input type="checkbox" id="showMode" name="showMode" {{if .Data.ShowMode}}checked{{end}}>
357+                        <input type="checkbox" id="showMode" name="showMode" {{if .Data.Settings.ShowMode}}checked{{end}}>
358                         Show File Modes
359                     </label>
360                 </div>
361@@ -15,7 +15,7 @@             </div>
362             <div class="field">
363                 <div class="control">
364                     <label class="checkbox">
365-                        <input type="checkbox" id="showOwner" name="showOwner" {{if .Data.ShowOwner}}checked{{end}}>
366+                        <input type="checkbox" id="showOwner" name="showOwner" {{if .Data.Settings.ShowOwner}}checked{{end}}>
367                         Show File Owner
368                     </label>
369                 </div>
370@@ -26,38 +26,19 @@             </div>
371         </form>
372     </div>
373     <div class="column">
374-        <div class="table-container">
375-            <table class="table">
376-                <thead>
377-                    <tr>
378-                        <th>Username</th>
379-                        <th>Path</th>
380-                        <th>Is Admin?</th>
381-                        <th>Action</th>
382-                    </tr>
383-                </thead>
384-                <tbody>
385-                    <tr>
386-                        <td>gabrielgio</td>
387-                        <td>/home/gabrielgio</td>
388-                        <td>yes</td>
389-                        <td>
390-                            <a href="#" class="button is-small">Edit</button>
391-                            <a href="#" class="button is-small is-danger">Delete</button>
392-                        </td>
393-                    </tr>
394-                    <tr>
395-                        <td>beatriz</td>
396-                        <td>/home/beatriz</td>
397-                        <td>no</td>
398-                        <td>
399-                            <a href="#" class="button is-small">Edit</button>
400-                            <a href="#" class="button is-small is-danger">Delete</button>
401-                        </td>
402-                    </tr>
403-                </tbody>
404-            </table>
405+        {{range .Data.Users}}
406+        <div class="panel-block">
407+            <div class="columns wide-column is-gapless is-mobile">
408+                <div class="column">
409+                    {{.Username}}
410+                </div>
411+                <div class="column">
412+                    {{.Path}}
413+                </div>
414+                <div class="column  has-text-right"><a href="#">Edit</a> / <a href="#" class="is-danger">Delete</a></div>
415+            </div>
416         </div>
417+        {{end}}
418     </div>
419 </div>
420 {{end}}