Compare commits
13 Commits
fa11926642
...
0af0129054
Author | SHA1 | Date |
---|---|---|
|
0af0129054 | |
|
8ff2ec3766 | |
|
843c71946f | |
|
51e3629fa5 | |
|
ddc003eb13 | |
|
e81a22dd10 | |
|
269cb2967c | |
|
6b764ae61b | |
|
44c89e6559 | |
|
a541a2dac2 | |
|
ff7baa0a24 | |
|
623d05da91 | |
|
8601288230 |
|
@ -25,4 +25,4 @@ run: $(GENERATED_FILES) $(OUTPUT_CSS)
|
||||||
# and then do `go run .` to rebuild all else
|
# and then do `go run .` to rebuild all else
|
||||||
.PHONY: run/live
|
.PHONY: run/live
|
||||||
run/live:
|
run/live:
|
||||||
wgo -verbose -file=.go -file=.templ make run
|
wgo -verbose -file=.go -file=.templ -file=$(INPUT_CSS) -file=$(TAILWIND_CONFIG) make run
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
rec {
|
rec {
|
||||||
description = "templ-practice";
|
description = "expenses chart exercise";
|
||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = "github:nixos/nixpkgs";
|
nixpkgs.url = "github:nixos/nixpkgs";
|
||||||
flake-utils.url = "github:numtide/flake-utils";
|
flake-utils.url = "github:numtide/flake-utils";
|
||||||
|
@ -11,7 +11,55 @@ rec {
|
||||||
let
|
let
|
||||||
pkgs = nixpkgs.legacyPackages.${system};
|
pkgs = nixpkgs.legacyPackages.${system};
|
||||||
templPkg = templ.packages.${system}.templ;
|
templPkg = templ.packages.${system}.templ;
|
||||||
in {
|
pname = "expenses-chart";
|
||||||
|
version = "0.0.1";
|
||||||
|
in rec {
|
||||||
|
thePackage = pkgs.buildGoModule {
|
||||||
|
inherit pname version;
|
||||||
|
src = pkgs.nix-gitignore.gitignoreSource [ ] ./.;
|
||||||
|
vendorHash = "sha256-sKuP3TsRD3MXBGtdSRnX62eXBQE1ASWFQu0kLlXSlFA=";
|
||||||
|
|
||||||
|
preBuild = ''
|
||||||
|
# Adding the Tailwind build step to preBuild
|
||||||
|
${pkgs.nodePackages.tailwindcss}/bin/tailwindcss -i ./input.css -o static/output.css
|
||||||
|
# Adding generation of go code from templ files
|
||||||
|
${templPkg}/bin/templ generate
|
||||||
|
'';
|
||||||
|
|
||||||
|
};
|
||||||
|
packages = rec {
|
||||||
|
"${pname}" = thePackage;
|
||||||
|
"${pname}-image" = pkgs.dockerTools.buildLayeredImage {
|
||||||
|
name = pname;
|
||||||
|
tag = "latest";
|
||||||
|
created = "now";
|
||||||
|
config = {
|
||||||
|
Cmd = [ "${thePackage}/bin/templ-exercise" ];
|
||||||
|
ExposedPorts = { "3000/tcp" = { }; };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
image-hello = pkgs.dockerTools.buildLayeredImage { # so, wow, this works
|
||||||
|
name = pname;
|
||||||
|
tag = "latest";
|
||||||
|
config.Cmd = [ "${pkgs.hello}/bin/hello" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
image = pkgs.dockerTools.buildImage {
|
||||||
|
name = pname;
|
||||||
|
tag = "latest";
|
||||||
|
created = "now";
|
||||||
|
copyToRoot = pkgs.buildEnv {
|
||||||
|
name = "image-root";
|
||||||
|
paths = [ thePackage pkgs.dockerTools.binSh pkgs.coreutils ];
|
||||||
|
pathsToLink = [ "/bin" "/dist" "/public" ];
|
||||||
|
};
|
||||||
|
config = {
|
||||||
|
Cmd = [ "/bin/templ-exercise" ];
|
||||||
|
ExposedPorts = { "8080/tcp" = { }; };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
devShells.default = pkgs.mkShell {
|
devShells.default = pkgs.mkShell {
|
||||||
buildInputs = [
|
buildInputs = [
|
||||||
pkgs.go
|
pkgs.go
|
||||||
|
@ -20,7 +68,7 @@ rec {
|
||||||
pkgs.gopls
|
pkgs.gopls
|
||||||
pkgs.gnumake
|
pkgs.gnumake
|
||||||
templPkg
|
templPkg
|
||||||
pkgs.tailwindcss
|
pkgs.nodePackages.tailwindcss
|
||||||
];
|
];
|
||||||
shellHook = ''
|
shellHook = ''
|
||||||
export GOPATH=$PWD/.go
|
export GOPATH=$PWD/.go
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
@tailwind base;
|
@tailwind base;
|
||||||
@tailwind components;
|
@tailwind components;
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
|
html {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
|
@ -1,17 +1,29 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"sunshine.industries/temp-exercise/templates"
|
"sunshine.industries/temp-exercise/templates"
|
||||||
"github.com/a-h/templ"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func randomDailyExpense() float32 {
|
||||||
|
return 10 + rand.Float32()*100
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
component := templates.Hello("some name")
|
|
||||||
|
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
http.Handle("/", templ.Handler(component))
|
component := templates.IndexPage(templates.PageData{
|
||||||
|
Balance: 500 + rand.Float32()*1000,
|
||||||
|
Expenses: []float32{randomDailyExpense(), randomDailyExpense(), randomDailyExpense(), randomDailyExpense(), randomDailyExpense(), randomDailyExpense(), randomDailyExpense()},
|
||||||
|
TotalThisMonth: 50 + rand.Float32()*600,
|
||||||
|
PercentComparedToLastMonth: -5 + 10*rand.Float32(),
|
||||||
|
})
|
||||||
|
component.Render(context.Background(), w)
|
||||||
|
})
|
||||||
|
|
||||||
staticFs := http.FileServer(http.Dir("./static"))
|
staticFs := http.FileServer(http.Dir("./static"))
|
||||||
http.Handle("/static/", http.StripPrefix("/static/", staticFs))
|
http.Handle("/static/", http.StripPrefix("/static/", staticFs))
|
||||||
|
|
|
@ -34,3 +34,74 @@ how would i commit to subtree upstream if there are conflicting changes?
|
||||||
maybe i'll just get denied, as simple as that.
|
maybe i'll just get denied, as simple as that.
|
||||||
|
|
||||||
allright! let's add exercise specific data (again)
|
allright! let's add exercise specific data (again)
|
||||||
|
|
||||||
|
* so, starting dev
|
||||||
|
|
||||||
|
old process:
|
||||||
|
1. open second browser and open png with style
|
||||||
|
first mobile
|
||||||
|
2. enable view C-S-m in firefox
|
||||||
|
the responsive mode
|
||||||
|
|
||||||
|
i suppose next is adding colors to tailwind configuration?
|
||||||
|
from the styleguide file
|
||||||
|
|
||||||
|
also
|
||||||
|
3. check desktop style, whether any big rearrangements are required
|
||||||
|
so that i'd start mobile with way to rearrange things
|
||||||
|
|
||||||
|
do i have a separate note with all of these things?
|
||||||
|
i've also used some commands to vertically center my stuff
|
||||||
|
|
||||||
|
** DONE allright, tailwind config go
|
||||||
|
font, size, colors
|
||||||
|
|
||||||
|
** well, i don't really want to go on.
|
||||||
|
but yeah, i'll need hierarchy of the elements.
|
||||||
|
|
||||||
|
** ok. thoughs:
|
||||||
|
the componets would be
|
||||||
|
- my balance bubble
|
||||||
|
because it is spacially separate
|
||||||
|
- spending summary
|
||||||
|
- graph inside spending summary
|
||||||
|
or maybe don't even need that
|
||||||
|
|
||||||
|
the component should take up all space provided to it.
|
||||||
|
if parent would want to add space around - that's easy to do,
|
||||||
|
but if component insists on adding space around itself - harder to reuse?
|
||||||
|
because then if parent would want to have child take up all space - negative padding?
|
||||||
|
|
||||||
|
*** allright, @ to include sub templates
|
||||||
|
|
||||||
|
** well, last part? preparing the docker container for deployment on Render.com?
|
||||||
|
|
||||||
|
which port should be used?
|
||||||
|
also, this would be first time i'll add templ as a build step
|
||||||
|
|
||||||
|
** for some reason image doesn't work well
|
||||||
|
|
||||||
|
podman run -d -p 9090:8080 localhost/expenses-chart:latest /nix/store/l4r6glmzbvkhg97lp4dn7nm76w6hz41g-expenses-chart-0.0.1/bin/temp-exercise
|
||||||
|
|
||||||
|
this runs,
|
||||||
|
but podman run -it expenses-chart:latest
|
||||||
|
Error: crun: executable file `/nix/store/l4r6glmzbvkhg97lp4dn7nm76w6hz41g-expenses-chart-0.0.1/bin/templ-exercise` not found in $PATH: No such file or directory: OCI runtime attempted to invoke a command that was not found
|
||||||
|
|
||||||
|
oh, well
|
||||||
|
|
||||||
|
** you know what - i give up
|
||||||
|
|
||||||
|
for some reason when i open url to app running in container,
|
||||||
|
and it's on the 8080 port of the host machine - the static files are served
|
||||||
|
|
||||||
|
if i use any other port of local machine, to forward into container,
|
||||||
|
the static files are not served
|
||||||
|
|
||||||
|
maybe this is bug in go? i don't know
|
||||||
|
StripPrefix doesn't seem to do anything
|
||||||
|
|
||||||
|
and for some reason in this specific exercise CMD doesn't work in the container
|
||||||
|
so let's just give up!
|
||||||
|
|
||||||
|
i could try to write a docker file and check with that, but yeah
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,28 @@
|
||||||
/** @type {import('tailwindcss').Config} */
|
/** @type {import('tailwindcss').Config} */
|
||||||
module.exports = {
|
module.exports = {
|
||||||
content: ["./**/*.templ"],
|
content: ["./templates/index.templ"],
|
||||||
theme: {
|
theme: {
|
||||||
extend: {},
|
extend: {
|
||||||
|
fontFamily: {
|
||||||
|
'sans': ['DM Sans', 'sans-serif'],
|
||||||
|
},
|
||||||
|
fontWeight: {
|
||||||
|
'normal': 400,
|
||||||
|
'bold': 700,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
colors: {
|
||||||
|
primary: {
|
||||||
|
'soft-red': 'hsl(10, 79%, 65%)',
|
||||||
|
cyan: 'hsl(186, 34%, 60%)',
|
||||||
|
},
|
||||||
|
neutral: {
|
||||||
|
'dark-brown': 'hsl(25, 47%, 15%)',
|
||||||
|
'medium-brown': 'hsl(28, 10%, 53%)',
|
||||||
|
cream: 'hsl(27, 66%, 92%)',
|
||||||
|
'very-pale-orange': 'hsl(33, 100%, 98%)',
|
||||||
|
},
|
||||||
|
}
|
||||||
},
|
},
|
||||||
plugins: [],
|
plugins: [],
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
package templates
|
|
||||||
|
|
||||||
var myVar string = "some string, changed. and more"
|
|
||||||
var anotherVar string = "hoho, cool"
|
|
||||||
|
|
||||||
templ Hello(name string) {
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>This is the title of the the webpage!</title>
|
|
||||||
<link href="/static/output.css" rel="stylesheet"/>
|
|
||||||
</head>
|
|
||||||
<body class="bg-blue-100 text-xl">
|
|
||||||
<p class="text-purple-700">This is an example paragraph. Anything in the <strong>body</strong> tag will appear on the page, just like this <strong>p</strong> tag and its contents.</p>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
}
|
|
|
@ -1,43 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- displays site properly based on user's device -->
|
|
||||||
|
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="./images/favicon-32x32.png">
|
|
||||||
|
|
||||||
<title>Frontend Mentor | Expenses chart component</title>
|
|
||||||
|
|
||||||
<!-- Feel free to remove these styles or customise in your own stylesheet 👍 -->
|
|
||||||
<style>
|
|
||||||
.attribution { font-size: 11px; text-align: center; }
|
|
||||||
.attribution a { color: hsl(228, 45%, 44%); }
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
My balance
|
|
||||||
$921.48
|
|
||||||
|
|
||||||
Spending - Last 7 days
|
|
||||||
|
|
||||||
mon
|
|
||||||
tue
|
|
||||||
wed
|
|
||||||
thu
|
|
||||||
fri
|
|
||||||
sat
|
|
||||||
sun
|
|
||||||
|
|
||||||
Total this month
|
|
||||||
$478.33
|
|
||||||
|
|
||||||
+2.4%
|
|
||||||
from last month
|
|
||||||
|
|
||||||
<div class="attribution">
|
|
||||||
Challenge by <a href="https://www.frontendmentor.io?ref=challenge" target="_blank">Frontend Mentor</a>.
|
|
||||||
Coded by <a href="#">Your Name Here</a>.
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -0,0 +1,133 @@
|
||||||
|
package templates
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
import "slices"
|
||||||
|
|
||||||
|
type PageData struct {
|
||||||
|
Balance float32
|
||||||
|
Expenses []float32
|
||||||
|
TotalThisMonth float32
|
||||||
|
PercentComparedToLastMonth float32
|
||||||
|
}
|
||||||
|
|
||||||
|
templ myBalanceComponent(balance float32) {
|
||||||
|
<div class="flex flex-row rounded-xl text-neutral-very-pale-orange text-xs md:text-base bg-primary-soft-red p-4 shadow md:rounded-[1rem] md:p-7">
|
||||||
|
<div class="grow ">
|
||||||
|
My balance
|
||||||
|
<p class="text-xl md:text-2xl font-bold">${ fmt.Sprintf("%.2f", balance) }</p>
|
||||||
|
</div>
|
||||||
|
<img
|
||||||
|
src="./static/images/logo.svg"
|
||||||
|
alt="App logo: a white circle overlapping from the left a filled in black circle"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
var days []string = []string{"mon", "tue", "wed", "thu", "fri", "sat", "sun"}
|
||||||
|
|
||||||
|
// returns templ SummaryComponent
|
||||||
|
// with all attributes computed from the expenses slice
|
||||||
|
// the percentages of the columns, current day number, etc
|
||||||
|
// NOTE: this seems to be the way to mix go calculations and templates
|
||||||
|
func prepareSummaryComponent(expenses []float32, totalThisMonth, percentComparedToLastMonth float32) templ.Component {
|
||||||
|
fmt.Println("hello, preparing expenses: ", expenses)
|
||||||
|
max := slices.Max(expenses)
|
||||||
|
percentages := make([]float32, 0, len(expenses))
|
||||||
|
for _, price := range expenses {
|
||||||
|
percentages = append(percentages, price/max)
|
||||||
|
}
|
||||||
|
|
||||||
|
currentDayNum := 2
|
||||||
|
|
||||||
|
return spendingSummaryComponent(expenses, percentages, currentDayNum, percentComparedToLastMonth, totalThisMonth)
|
||||||
|
}
|
||||||
|
|
||||||
|
css expenseBarVars(percentage float32) {
|
||||||
|
--height-percentage: { fmt.Sprintf("%.2f", percentage) };
|
||||||
|
height: calc(var(--height-percentage) * 8rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
templ dayExpenseColumn(expense, percentage float32, day string, isCurrentDay bool) {
|
||||||
|
<div class="flex flex-col justify-end items-center h-32">
|
||||||
|
<div
|
||||||
|
class={ "rounded group relative w-full h-12",
|
||||||
|
expenseBarVars(percentage),
|
||||||
|
templ.KV("bg-primary-soft-red hover:bg-primary-soft-red/70", !isCurrentDay),
|
||||||
|
templ.KV("bg-primary-cyan hover:bg-primary-cyan/70", isCurrentDay),
|
||||||
|
"hover:cursor-pointer" }
|
||||||
|
>
|
||||||
|
<span class="absolute left-1/2 -translate-x-1/2 -translate-y-7 opacity-0 group-hover:opacity-100 bg-neutral-dark-brown text-neutral-cream rounded text-xs p-1">${ fmt.Sprintf("%.2f", expense ) }</span>
|
||||||
|
</div>
|
||||||
|
<p class="text-neutral-medium-brown text-xs py-2">{ day }</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
// The 7 vertical bars of the per-day expenses
|
||||||
|
templ expensesChart(expenses, percentages []float32, currentDayNum int) {
|
||||||
|
<div class="grid grid-cols-7 place-content-between gap-x-3">
|
||||||
|
for i := 0; i < 7; i++ {
|
||||||
|
@dayExpenseColumn(expenses[i], percentages[i], days[i], currentDayNum == i)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
// Big container with chard and total expenses of the week
|
||||||
|
templ spendingSummaryComponent(expenses, percentages []float32, currentDayNum int, totalThisMonth, percentComparedToLastMonth float32) {
|
||||||
|
<div class="bg-neutral-very-pale-orange rounded-xl shadow p-4 py-6 flex flex-col gap-y-4 md:p-7 md:px-10 md:rounded-[1rem] md:gap-y-5">
|
||||||
|
<p2 class="text-neutral-dark-brown text-xl font-bold pb-6 md:text-2xl md:pb-12 ">Spending - Last 7 days</p2>
|
||||||
|
@expensesChart(expenses, percentages, currentDayNum)
|
||||||
|
<hr class="bg-neutral-cream border-t-0 h-0.5"/>
|
||||||
|
<div class="grid grid-cols-2 text-sm text-neutral-medium-brown">
|
||||||
|
<p class="col-span-full">Total this month</p>
|
||||||
|
<div class="grow">
|
||||||
|
<p class="grid items-center text-neutral-dark-brown text-2xl font-bold h-full md:text-4xl md:py-2">$ { fmt.Sprintf("%.2f", totalThisMonth) }</p>
|
||||||
|
</div>
|
||||||
|
<div class="text-right grid content-center">
|
||||||
|
<div class="text-neutral-dark-brown font-bold ">
|
||||||
|
if (percentComparedToLastMonth > 0) {
|
||||||
|
<span class="inline">+</span>
|
||||||
|
}
|
||||||
|
{ fmt.Sprintf("%.2f", percentComparedToLastMonth) }%
|
||||||
|
</div>
|
||||||
|
from last month
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
templ WidgetsComponend(pageData PageData) {
|
||||||
|
<div class="flex flex-col gap-4">
|
||||||
|
@myBalanceComponent(pageData.Balance)
|
||||||
|
@prepareSummaryComponent(pageData.Expenses, pageData.PercentComparedToLastMonth, pageData.TotalThisMonth)
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
templ IndexPage(pageData PageData) {
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com"/>
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin/>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,100..1000;1,9..40,100..1000&display=swap" rel="stylesheet"/>
|
||||||
|
<link rel="stylesheet" href="/styles/templ.css"/>
|
||||||
|
<link href="/static/output.css" rel="stylesheet"/>
|
||||||
|
<meta charset="UTF-8"/>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/> <!-- displays site properly based on user's device -->
|
||||||
|
<link rel="icon" type="image/png" sizes="32x32" href="./static/images/favicon-32x32.png"/>
|
||||||
|
<title>Frontend Mentor | Expenses chart component</title>
|
||||||
|
<!-- Feel free to remove these styles or customise in your own stylesheet 👍 -->
|
||||||
|
<style>
|
||||||
|
.attribution { font-size: 11px; text-align: center; }
|
||||||
|
.attribution a { color: hsl(228, 45%, 44%); }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body class="bg-neutral-cream text-bold p-4 h-full grid place-items-center">
|
||||||
|
<main class="w-full md:w-1/4">
|
||||||
|
@WidgetsComponend(pageData)
|
||||||
|
</main>
|
||||||
|
<div class="attribution fixed bottom-0 inset-x-0">
|
||||||
|
Challenge by <a href="https://www.frontendmentor.io?ref=challenge" target="_blank">Frontend Mentor</a>.
|
||||||
|
Coded by <a href="#">Your Name Here</a>.
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
}
|
Loading…
Reference in New Issue