Intermediate commit
- Following info is now parsed from yaml, specified with --config flag default path for config: ./data/resume.yaml - Summary - Experience - Education - Skills - Languages Need to update sample YAML with mock data
This commit is contained in:
parent
67215cf921
commit
8b7978f7b5
|
@ -0,0 +1 @@
|
|||
resume.yaml
|
27
main.go
27
main.go
|
@ -2,9 +2,12 @@ package main
|
|||
|
||||
import (
|
||||
"embed"
|
||||
"flag"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"os"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -13,9 +16,18 @@ import (
|
|||
//go:embed "static/css/*.css" "templates/*.html"
|
||||
var static embed.FS
|
||||
var templates map[string]*template.Template
|
||||
var configFile string
|
||||
|
||||
func main() {
|
||||
readConfig("")
|
||||
cfgFile := flag.String("config", "./data/resume.yaml", "Path to the configuration YAML")
|
||||
flag.Parse()
|
||||
|
||||
_, err := readConfig(*cfgFile)
|
||||
if err != nil {
|
||||
slog.Error(fmt.Sprintf("error reading configuration: %s", err.Error()))
|
||||
os.Exit(1)
|
||||
}
|
||||
configFile = *cfgFile
|
||||
staticFileServer := http.FileServer(http.FS(static))
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/", home)
|
||||
|
@ -24,7 +36,7 @@ func main() {
|
|||
mux.Handle("/static/", staticFileServer)
|
||||
|
||||
slog.Info("Starting go-resume server, listening on port 3000")
|
||||
err := http.ListenAndServe(":3000", mux)
|
||||
err = http.ListenAndServe("0.0.0.0:3000", mux)
|
||||
slog.Error(err.Error())
|
||||
}
|
||||
|
||||
|
@ -43,7 +55,7 @@ func home(w http.ResponseWriter, r *http.Request) {
|
|||
slog.Error(err.Error())
|
||||
http.Error(w, "Server error", http.StatusInternalServerError)
|
||||
}
|
||||
data, err := readConfig("")
|
||||
data, err := readConfig(configFile)
|
||||
if err != nil {
|
||||
slog.Error(err.Error())
|
||||
http.Error(w, "Server error", http.StatusInternalServerError)
|
||||
|
@ -54,9 +66,18 @@ func home(w http.ResponseWriter, r *http.Request) {
|
|||
data.Theme = urlSlice[len(urlSlice)-1]
|
||||
}
|
||||
data.Year = time.Now().Format("2006")
|
||||
tmpl.Funcs(templateFunctions())
|
||||
err = tmpl.Execute(w, *data)
|
||||
if err != nil {
|
||||
slog.Error(err.Error())
|
||||
http.Error(w, "Server error", http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
func templateFunctions() template.FuncMap {
|
||||
return template.FuncMap{
|
||||
"html": func(raw string) template.HTML {
|
||||
return template.HTML(raw)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
91
resume.go
91
resume.go
|
@ -1,5 +1,10 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
)
|
||||
|
||||
type Resume struct {
|
||||
Theme string `yaml:"theme"`
|
||||
Meta *Meta `yaml:"meta"`
|
||||
|
@ -8,16 +13,45 @@ type Resume struct {
|
|||
}
|
||||
|
||||
type Profile struct {
|
||||
Name string `yaml:"name"`
|
||||
Title string `yaml:"title"`
|
||||
City string `yaml:"city"`
|
||||
Phone string `yaml:"phone"`
|
||||
Email string `yaml:"email"`
|
||||
Socials Socials `yaml:"socials"`
|
||||
Photo string `yaml:"photo"`
|
||||
Name string `yaml:"name"`
|
||||
Title string `yaml:"title"`
|
||||
City string `yaml:"city"`
|
||||
Phone string `yaml:"phone"`
|
||||
Email string `yaml:"email"`
|
||||
Socials Socials `yaml:"socials"`
|
||||
Photo string `yaml:"photo"`
|
||||
Summary string `yaml:"summary"`
|
||||
Experience []Job `yaml:"experience"`
|
||||
Education []Education `yaml:"education"`
|
||||
Skills []Skill `yaml:"skills"`
|
||||
Projects any `yaml:"projects"`
|
||||
Languages []Language `yaml:"languages"`
|
||||
}
|
||||
|
||||
type job struct {
|
||||
func (p *Profile) GetTagWidth(t string) string {
|
||||
l := 0
|
||||
switch t {
|
||||
case "skills":
|
||||
for _, skill := range p.Skills {
|
||||
if len(skill.Name) > l {
|
||||
l = len(skill.Name)
|
||||
}
|
||||
}
|
||||
case "languages":
|
||||
for _, skill := range p.Languages {
|
||||
if len(skill.Name) > l {
|
||||
l = len(skill.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("%dch", l)
|
||||
}
|
||||
|
||||
func (job *Job) html(raw string) template.HTML {
|
||||
return template.HTML(raw)
|
||||
}
|
||||
|
||||
type Job struct {
|
||||
Company string `yaml:"company"`
|
||||
Location string `yaml:"location"`
|
||||
Title string `yaml:"title"`
|
||||
|
@ -25,24 +59,40 @@ type job struct {
|
|||
Description []string `yaml:"description"`
|
||||
}
|
||||
|
||||
type jobs []job
|
||||
|
||||
type education struct {
|
||||
type Education struct {
|
||||
Name string `yaml:"name"`
|
||||
City string `yaml:"city"`
|
||||
Degree string `yaml:"degree"`
|
||||
Period string `yaml:"period"`
|
||||
Faculty string `yaml:"faculty"`
|
||||
City string `yaml:"city"`
|
||||
Period string `yaml:"period"`
|
||||
}
|
||||
|
||||
type educations []education
|
||||
|
||||
type skill struct {
|
||||
type Skill struct {
|
||||
Name string `yaml:"name"`
|
||||
Level string `yaml:"level"`
|
||||
Level int `yaml:"level"`
|
||||
}
|
||||
|
||||
type skills []skill
|
||||
func _visualLevel(level int) template.HTML {
|
||||
tagString := ""
|
||||
if level > 5 {
|
||||
level = 5
|
||||
} else if level < 1 {
|
||||
level = 1
|
||||
}
|
||||
for i := 0; i < level; i++ {
|
||||
tagString += "<span class=\"tag is-info is-success is-size-6\" style=\"color: black\">☆</span>"
|
||||
}
|
||||
return template.HTML(tagString)
|
||||
}
|
||||
func (skill *Skill) VisualLevel(level int) template.HTML {
|
||||
return _visualLevel(level)
|
||||
}
|
||||
|
||||
func (language *Language) VisualLevel(level int) template.HTML {
|
||||
return _visualLevel(level)
|
||||
}
|
||||
|
||||
type skills []Skill
|
||||
|
||||
type Social struct {
|
||||
Media string `yaml:"media"`
|
||||
|
@ -60,3 +110,8 @@ type Meta struct {
|
|||
Robots string `yaml:"robots"`
|
||||
ThemeColor string `yaml:"theme-color"`
|
||||
}
|
||||
|
||||
type Language struct {
|
||||
Name string `yaml:"name"`
|
||||
Level int `yaml:"level"`
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ body {
|
|||
.education .item,
|
||||
.projects .item,
|
||||
.skills .item,
|
||||
.languages .item,
|
||||
.awards .item {
|
||||
padding: .5em 0; }
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ body {
|
|||
.education,
|
||||
.projects,
|
||||
.skills,
|
||||
.languages,
|
||||
.awards {
|
||||
.item {
|
||||
padding: .5em 0;
|
||||
|
|
|
@ -38,6 +38,7 @@ body {
|
|||
.education .item,
|
||||
.projects .item,
|
||||
.skills .item,
|
||||
.languages .item,
|
||||
.awards .item {
|
||||
padding: .5em 0; }
|
||||
|
||||
|
|
|
@ -4,6 +4,22 @@
|
|||
<head>
|
||||
{{ template "meta" . }}
|
||||
<link rel="stylesheet" href="/static/css/{{ .Theme }}-style.css" />
|
||||
|
||||
<style>
|
||||
{{ if .Profile.Skills }}
|
||||
.skilltag {
|
||||
display: inline-block;
|
||||
width: {{ .Profile.GetTagWidth "skills" }};
|
||||
}
|
||||
{{ end }}
|
||||
{{ if .Profile.Languages }}
|
||||
.languagetag {
|
||||
display: inline-block;
|
||||
width: {{ .Profile.GetTagWidth "languages" }};
|
||||
}
|
||||
{{ end }}
|
||||
</style>
|
||||
|
||||
</head>
|
||||
|
||||
<body id="body-app">
|
||||
|
@ -21,7 +37,7 @@
|
|||
</div>
|
||||
<div class="column is-2">
|
||||
<figure class="image image is-128x128">
|
||||
<img alt="Profile photo" class="is-rounded" src="{{ .Profile.Photo }}" /> <!--<%= content.data.profile.photo %>" />-->
|
||||
<img alt="Profile photo" class="is-rounded" src="{{ .Profile.Photo }}" />
|
||||
</figure>
|
||||
</div>
|
||||
<div class="column has-text-grey-light has-text-right-in-desktop">
|
||||
|
@ -55,55 +71,72 @@
|
|||
<div class="title is-size-5 has-text-primary has-text-weight-bold">
|
||||
SUMMARY
|
||||
</div>
|
||||
<div class="wrapper"><%- content.data.summary %></div>
|
||||
<div class="wrapper">{{ .Profile.Summary }}</div>
|
||||
</div>
|
||||
|
||||
{{ with .Profile.Experience }}
|
||||
<div class="experience">
|
||||
<div class="title is-size-5 has-text-primary has-text-weight-bold">
|
||||
EXPERIENCE
|
||||
</div>
|
||||
|
||||
<% content.data.experiences.forEach(function(experience){ %>
|
||||
<!--<% content.data.experiences.forEach(function(experience){ %>-->
|
||||
{{ range . }}
|
||||
<div class="item">
|
||||
<div class="is-size-5">
|
||||
<b><%= experience.company %>,</b>
|
||||
<span class="has-text-weight-semi-bold">
|
||||
<%= experience.location %> — <i><%= experience.title %></i>
|
||||
</span>
|
||||
<!-- <div class="columns is-gapless">-->
|
||||
<!-- <div class="column">-->
|
||||
<span><b>{{ .Company }}</b></span>
|
||||
<!-- </div>-->
|
||||
<!-- <div class="column">-->
|
||||
<span class="has-text-weight-semi-bold"> - <i>{{ .Title }}</i></span>
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
</div>
|
||||
<div class="is-size-7 tag is-primary"><%= experience.period %></div>
|
||||
<% if (experience.jobdesc.length > 0) { %>
|
||||
<div>{{ .Location }}</div>
|
||||
<div class="is-size-6 tag is-primary">{{ .Period }}</div>
|
||||
{{ with .Description }}
|
||||
<ul style="list-style: disc; margin-left: 1em">
|
||||
<% experience.jobdesc.forEach(function(job){ %>
|
||||
<li><%= job %></li>
|
||||
<% }); %>
|
||||
{{ range . }}
|
||||
<li>{{ . | html }}</li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
<% } %>
|
||||
{{ end }}
|
||||
</div>
|
||||
<% }); %>
|
||||
{{ end }}
|
||||
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
{{ with .Profile.Education }}
|
||||
<div class="education">
|
||||
<div class="title is-size-5 has-text-primary has-text-weight-bold">
|
||||
EDUCATION
|
||||
</div>
|
||||
|
||||
<% content.data.educations.forEach(function(education){ %>
|
||||
{{ range . }}
|
||||
<div class="item">
|
||||
<div class="is-size-5">
|
||||
<b><%= education.name %></b>,
|
||||
<span class="has-text-weight-semi-bold">
|
||||
<%= education.city %> — <i><%= education.degree %></i>
|
||||
</span>
|
||||
<!-- <div class="columns is-gapless">-->
|
||||
<!-- <div class="column">-->
|
||||
<span><b>{{ .Degree }}</b>, </span>
|
||||
<!-- </div>-->
|
||||
<!-- <div class="column">-->
|
||||
<span class="has-text-weight-semi-bold">{{ .Name }}</span>
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
</div>
|
||||
<div>{{ .Faculty }}
|
||||
<div class="is-size-6 tag is-primary ml-5">{{ .Period }}</div>
|
||||
</div>
|
||||
<div class="is-size-7 tag is-primary"><%= education.period %></div>
|
||||
<div><%= education.faculty %></div>
|
||||
</div>
|
||||
<% }); %>
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ if or .Profile.Projects (or .Profile.Skills .Profile.Languages) }}
|
||||
<div class="column">
|
||||
{{ with .Profile.Projects }}
|
||||
<div class="projects">
|
||||
<div class="title is-size-5 has-text-primary has-text-weight-bold">
|
||||
PROJECTS
|
||||
|
@ -116,34 +149,57 @@
|
|||
—
|
||||
<i><%= proj.company %></i>
|
||||
</div>
|
||||
<div class="is-size-7 tag is-primary"><%= proj.period %></div>
|
||||
<div class="is-size-6 tag is-primary"><%= proj.period %></div>
|
||||
</div>
|
||||
<% }); %>
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
{{ with .Profile.Skills }}
|
||||
<div class="skills">
|
||||
<div class="title is-size-5 has-text-primary has-text-weight-bold">
|
||||
SKILLS
|
||||
</div>
|
||||
<div class="wrapper">
|
||||
<% content.data.skills.forEach(function(skill){ %>
|
||||
{{ range . }}
|
||||
<div class="item">
|
||||
<div><%= skill.name %></div>
|
||||
<div class="field is-grouped is-grouped-multiline">
|
||||
<div class="control">
|
||||
<div class="tags has-addons">
|
||||
<span class="skilltag tag is-size-6">{{ .Name }}</span>
|
||||
{{ .VisualLevel .Level }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% }); %>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
{{ with .Profile.Languages }}
|
||||
<div class="languages">
|
||||
<div class="title is-size-5 has-text-primary has-text-weight-bold">
|
||||
LANGUAGES
|
||||
</div>
|
||||
<div class="wrapper">
|
||||
<%= content.data.languages.join(', ') %>
|
||||
{{ range . }}
|
||||
<div class="item">
|
||||
<div class="field is-grouped is-grouped-multiline">
|
||||
<div class="control">
|
||||
<div class="tags has-addons">
|
||||
<span class="languagetag tag is-size-6">{{ .Name }}</span>
|
||||
{{ .VisualLevel .Level }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
|
Loading…
Reference in New Issue