Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 3c994ec

Browse files
committedApr 23, 2024
init
0 parents  commit 3c994ec

8 files changed

+384
-0
lines changed
 

‎go.mod

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module main
2+
3+
go 1.22.2
4+
5+
require github.com/google/uuid v1.6.0 // indirect

‎go.sum

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
2+
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=

‎main.go

+169
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"html/template"
6+
"net/http"
7+
"strconv"
8+
"strings"
9+
10+
"github.com/google/uuid"
11+
)
12+
13+
type ProjectStep struct {
14+
Id string
15+
ProjectId string
16+
StepNumber int
17+
Description string
18+
}
19+
20+
func (ps *ProjectStep) ToString() string {
21+
//fmt.Sprintf("ProjectStep {Id: ()\n\tProjectId: ()\n\tStepNumber: ()\n\tDescription: ()\n\t}")
22+
return fmt.Sprintf("%#v", ps)
23+
}
24+
25+
func ProjectStepsToString(ps []ProjectStep) string {
26+
sb := strings.Builder{}
27+
for i, p := range ps {
28+
sb.WriteString(fmt.Sprint(i) + ": " + p.ToString() + "\n")
29+
}
30+
31+
return sb.String()
32+
}
33+
34+
type Project struct {
35+
Name string
36+
Description string
37+
Id string
38+
Steps []ProjectStep
39+
WithEditRow bool
40+
}
41+
42+
func (p *Project) ToString() string {
43+
return "Project {\n\tName: " + p.Name + "\n\tDescription: " + p.Description + "\n\tId: " + p.Id + "\n\tCompletionSteps: []ProjectStep{" + ProjectStepsToString(p.Steps) + "}\n}"
44+
}
45+
46+
type ProjectViewModel struct {
47+
Projects []Project
48+
SelectedProject Project
49+
}
50+
51+
func panicIfErr(e error) {
52+
if e != nil {
53+
panic(e)
54+
}
55+
}
56+
57+
func parse(x string) (int, error) {
58+
return strconv.Atoi(x)
59+
}
60+
61+
func add(x, y int) string {
62+
return strconv.Itoa(x + y)
63+
64+
}
65+
66+
func main() {
67+
mux := http.NewServeMux()
68+
repo := NewRepository()
69+
funcMap := template.FuncMap{
70+
"add": add,
71+
"parse": parse,
72+
}
73+
74+
templates := template.Must(template.New("stupidfuckingbitch").Funcs(funcMap).ParseFiles("main.html", "projects-list.html", "projects-description.html", "projects-steps-table.html"))
75+
mux.HandleFunc("POST /projects/", func(w http.ResponseWriter, r *http.Request) {
76+
fmt.Println("Matched POST /projects")
77+
p := Project{
78+
Id: uuid.NewString(),
79+
Name: r.FormValue("project-name"),
80+
Description: r.FormValue("project-description"),
81+
}
82+
repo.AddProject(p)
83+
http.Redirect(w, r, r.Referer(), http.StatusSeeOther)
84+
})
85+
86+
mux.HandleFunc("DELETE /projects/{id}", func(w http.ResponseWriter, r *http.Request) {
87+
fmt.Println("Matched DELETE /projects/{id}")
88+
projectId := r.PathValue("id")
89+
repo.RemoveProject(projectId)
90+
http.Redirect(w, r, "/", http.StatusSeeOther)
91+
})
92+
93+
mux.HandleFunc("GET /", func(w http.ResponseWriter, r *http.Request) {
94+
fmt.Println("Matched GET /")
95+
selectedProject, err := repo.GetProjectByIndex(0)
96+
fmt.Println("Selected Project is " + selectedProject.ToString())
97+
if err != nil {
98+
panic(err)
99+
}
100+
templates.ExecuteTemplate(w, "base", ProjectViewModel{Projects: repo.GetAllProjects(), SelectedProject: selectedProject})
101+
})
102+
103+
mux.HandleFunc("GET /projects/{selectedId}", func(w http.ResponseWriter, r *http.Request) {
104+
selectedId := r.PathValue("selectedId")
105+
fmt.Println("Matched GET /{selectedId}")
106+
selectedProj, err := repo.GetProjectById(selectedId)
107+
if err != nil {
108+
http.NotFound(w, r)
109+
}
110+
projects := repo.GetAllProjects()
111+
112+
templates.ExecuteTemplate(w, "base", ProjectViewModel{Projects: projects, SelectedProject: selectedProj})
113+
})
114+
115+
mux.HandleFunc("GET /projects/{id}/description", func(w http.ResponseWriter, r *http.Request) {
116+
projectId := r.PathValue("id")
117+
fmt.Println("Matched GET /projects/" + projectId + "/description")
118+
proj, err := repo.GetProjectById(projectId)
119+
if err != nil {
120+
panic(err)
121+
}
122+
123+
fmt.Fprint(w, proj.Description)
124+
})
125+
126+
mux.HandleFunc("PUT /projects/{id}/description", func(w http.ResponseWriter, r *http.Request) {
127+
fmt.Println("Matched PUT /projects/{id}/description")
128+
projDescription := r.FormValue("project-description")
129+
projId := r.PathValue("id")
130+
repo.SetProjectDescription(projId, projDescription)
131+
fmt.Fprint(w, http.StatusOK)
132+
})
133+
134+
mux.HandleFunc("GET /project/{id}/steps/", func(w http.ResponseWriter, r *http.Request) {
135+
projectId := r.PathValue("id")
136+
fmt.Println("Matched GET /project/" + projectId + "/steps/")
137+
wer := r.FormValue("WithEditRow")
138+
WithEditRow, err := strconv.ParseBool(wer)
139+
fmt.Println("WithEditRow: " + wer)
140+
if err != nil {
141+
WithEditRow = false
142+
}
143+
proj, err := repo.GetProjectById(projectId)
144+
panicIfErr(err)
145+
146+
proj.WithEditRow = WithEditRow
147+
148+
newerr := templates.ExecuteTemplate(w, "projectsStepsTable", ProjectViewModel{SelectedProject: proj})
149+
if newerr != nil {
150+
fmt.Fprint(w, "<div style=\"color: red\">"+newerr.Error()+"</div>")
151+
}
152+
})
153+
154+
mux.HandleFunc("POST /project/{id}/steps/", func(w http.ResponseWriter, r *http.Request) {
155+
fmt.Println("Matched POST /projectsteps/")
156+
projectId := r.PathValue("id")
157+
sn := r.FormValue("stepNumber")
158+
Description := r.FormValue("Description")
159+
stepNumber, err := strconv.Atoi(sn)
160+
panicIfErr(err)
161+
step := ProjectStep{Id: uuid.NewString(), ProjectId: projectId, StepNumber: stepNumber, Description: Description}
162+
repo.AddStepToProject(projectId, step)
163+
http.Redirect(w, r, "/projects/"+projectId, http.StatusSeeOther)
164+
})
165+
166+
port := ":8080"
167+
fmt.Println("Listening on http://localhost" + port)
168+
http.ListenAndServe(port, mux)
169+
}

‎main.html

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{{define "base"}}
2+
3+
<!DOCTYPE html>
4+
<html lang="en" height="100%" width="100%" data-bs-theme="dark">
5+
<head>
6+
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
7+
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
8+
<script src="https://unpkg.com/htmx.org@1.9.12" integrity="sha384-ujb1lZYygJmzgSwoxRggbCHcjc0rB2XoQrxeTUQyRjrOnlCoYta87iKBWq3EsdM2" crossorigin="anonymous"></script>
9+
<script src="https://unpkg.com/htmx.org@1.9.12/dist/ext/debug.js"></script>
10+
<title>Go Server</title>
11+
<style>
12+
.col-6 {
13+
padding: 2rem;
14+
}
15+
</style>
16+
</head>
17+
<body>
18+
<button hx-get="/" hx-target="body" hx-swap="innerHTML" hx-push-url="true">
19+
Home
20+
</button>
21+
<div class="row">
22+
<div class="col-6" id="projects-list">
23+
{{ template "projectsList" . }}
24+
</div>
25+
<div class="col-6">
26+
{{ template "projectsDescription" . }}
27+
</div>
28+
</div>
29+
</body>
30+
</html>
31+
{{end}}

‎projects-description.html

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{{define "projectsDescription"}}
2+
<div class="row">
3+
<textarea name="project-description" id="project-description" cols="30" rows="10" hx-put="/projects/{{.SelectedProject.Id}}/description" hx-trigger="input delay:500ms">{{.SelectedProject.Description}}</textarea>
4+
{{ template "projectsStepsTable" .}}
5+
</div>
6+
{{end}}

‎projects-list.html

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{{ define "projectsList" }}
2+
<form action="/projects/" method="POST">
3+
<input type="text" name="project-name" id="project-name">
4+
<button>Create new project</button>
5+
</form>
6+
7+
<div id="projects">
8+
<ul>
9+
{{range .Projects}}
10+
<li>
11+
{{if eq $.SelectedProject.Id .Id}}
12+
<div style="text-decoration: underline; display: inline">{{.Name}}</div>
13+
{{else}}
14+
<button hx-get="/projects/{{.Id}}" hx-target="body" hx-push-url="true">{{.Name}}</button>
15+
{{end}}
16+
{{if gt (len $.Projects) 1 }}
17+
<button style="color: red" hx-delete="/projects/{{.Id}}" hx-swap="innerHTML" hx-target="body">X</button>
18+
{{end}}
19+
<input type="hidden" name="project-id" id="project-id" value={{.Id}}>
20+
</li>
21+
{{end}}
22+
</ul>
23+
</div>
24+
{{end}}

‎projects-steps-table.html

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
{{define "projectsStepsTable"}}
2+
<table class="table">
3+
<thead>
4+
<th>Step</th>
5+
<th>Description</th>
6+
<th></th>
7+
</thead>
8+
<tbody>
9+
{{range .SelectedProject.Steps }}
10+
<tr>
11+
<td>{{.StepNumber}}</td>
12+
<td>{{.Description}}</td>
13+
<td></td>
14+
</tr>
15+
{{end}}
16+
17+
18+
{{ if .SelectedProject.WithEditRow }}
19+
<tr>
20+
<form action="/project/{{.SelectedProject.Id}}/steps/" method="POST">
21+
<td>
22+
<input type="number" name="stepNumber" id="stepNumber" min="{{add (len .SelectedProject.Steps) 1 }}" value="{{add (len .SelectedProject.Steps) 1 }}">
23+
</td>
24+
<td>
25+
<input type="text" name="Description" id="Description" required="true">
26+
</td>
27+
<td>
28+
<button>Submit</button>
29+
</td>
30+
</form>
31+
</tr>
32+
{{ else }}
33+
<tr>
34+
<td></td>
35+
<td>
36+
<button hx-get="/project/{{.SelectedProject.Id}}/steps/?WithEditRow=true" hx-target="closest table" hx-swap="outerHTML">+</button>
37+
</td>
38+
<td></td>
39+
</tr>
40+
{{ end }}
41+
</tbody>
42+
</table>
43+
{{end}}

‎repository.go

+104
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package main
2+
3+
import (
4+
"errors"
5+
6+
"github.com/google/uuid"
7+
)
8+
9+
type Repository struct {
10+
projects []Project
11+
projectSteps []ProjectStep
12+
}
13+
14+
func NewRepository() Repository {
15+
r := Repository{}
16+
r.Init()
17+
return r
18+
}
19+
20+
func (r *Repository) GetAllProjects() []Project {
21+
return r.projects
22+
}
23+
24+
func (r *Repository) GetProjectSteps(projectId string) []ProjectStep {
25+
items := []ProjectStep{}
26+
27+
for _, ps := range r.projectSteps {
28+
if ps.ProjectId == projectId {
29+
items = append(items, ps)
30+
}
31+
}
32+
33+
return items
34+
}
35+
36+
func (r *Repository) Init() {
37+
p := Project{"TestProj1", "This is a description for a thing that is being described", uuid.NewString(), []ProjectStep{}, false}
38+
ps := []ProjectStep{
39+
{uuid.NewString(), p.Id, 1, "Step Description"},
40+
{uuid.NewString(), p.Id, 2, "Step Description 2"},
41+
}
42+
p.Steps = ps
43+
r.projects = append(r.projects, p)
44+
}
45+
46+
func (r *Repository) AddProject(p Project) {
47+
r.projects = append(r.projects, p)
48+
}
49+
50+
func (r *Repository) RemoveProject(id string) {
51+
if len(r.projects) == 1 {
52+
return
53+
}
54+
tempProjects := []Project{}
55+
56+
for _, p := range r.projects {
57+
if p.Id != id {
58+
tempProjects = append(tempProjects, p)
59+
}
60+
}
61+
62+
r.projects = tempProjects
63+
}
64+
65+
func (r *Repository) GetProjectById(id string) (Project, error) {
66+
for i, p := range r.projects {
67+
if p.Id == id {
68+
return r.projects[i], nil
69+
}
70+
}
71+
72+
return Project{}, errors.New("could not find project")
73+
}
74+
75+
func (r *Repository) GetProjectByIndex(idx int) (Project, error) {
76+
if idx >= len(r.projects) {
77+
return Project{}, errors.New("index too large")
78+
}
79+
80+
return r.projects[idx], nil
81+
}
82+
83+
func (r *Repository) SetProjectDescription(projectId, desc string) {
84+
for i, p := range r.projects {
85+
if p.Id == projectId {
86+
r.projects[i].Description = desc
87+
}
88+
}
89+
}
90+
91+
func (r *Repository) AddStepToProject(projectId string, step ProjectStep) (bool, error) {
92+
_, err := r.GetProjectById(projectId)
93+
if err != nil {
94+
return false, err
95+
}
96+
97+
for i, p := range r.projects {
98+
if p.Id == projectId {
99+
r.projects[i].Steps = append(r.projects[i].Steps, step)
100+
}
101+
}
102+
103+
return true, nil
104+
}

0 commit comments

Comments
 (0)
Please sign in to comment.