First draft of elm frontend
This will probably be scrapped or rewritten
This commit is contained in:
		
							parent
							
								
									e9998b9e8e
								
							
						
					
					
						commit
						afd7e9369d
					
				| @ -35,3 +35,4 @@ ssh -nNTvL 5432:fsmi-db.fsmi.org:5432 fsmi-login.fsmi.uni-karlsruhe.de | |||||||
|   - [ ] etc. |   - [ ] etc. | ||||||
|   - [ ] Make it print nicely |   - [ ] Make it print nicely | ||||||
| - [ ] Make it possible to edit entries | - [ ] Make it possible to edit entries | ||||||
|  | - [ ] Improve project structure | ||||||
|  | |||||||
							
								
								
									
										28
									
								
								elm.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								elm.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,28 @@ | |||||||
|  | { | ||||||
|  |     "type": "application", | ||||||
|  |     "source-directories": [ | ||||||
|  |         "frontend" | ||||||
|  |     ], | ||||||
|  |     "elm-version": "0.19.1", | ||||||
|  |     "dependencies": { | ||||||
|  |         "direct": { | ||||||
|  |             "NoRedInk/elm-json-decode-pipeline": "1.0.1", | ||||||
|  |             "elm/browser": "1.0.2", | ||||||
|  |             "elm/core": "1.0.5", | ||||||
|  |             "elm/html": "1.0.0", | ||||||
|  |             "elm/http": "2.0.0", | ||||||
|  |             "elm/json": "1.1.3" | ||||||
|  |         }, | ||||||
|  |         "indirect": { | ||||||
|  |             "elm/bytes": "1.0.8", | ||||||
|  |             "elm/file": "1.0.5", | ||||||
|  |             "elm/time": "1.0.0", | ||||||
|  |             "elm/url": "1.0.0", | ||||||
|  |             "elm/virtual-dom": "1.0.3" | ||||||
|  |         } | ||||||
|  |     }, | ||||||
|  |     "test-dependencies": { | ||||||
|  |         "direct": {}, | ||||||
|  |         "indirect": {} | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										292
									
								
								frontend/Entry.elm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										292
									
								
								frontend/Entry.elm
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,292 @@ | |||||||
|  | module Entry exposing (main) | ||||||
|  | 
 | ||||||
|  | import Browser | ||||||
|  | import Http | ||||||
|  | 
 | ||||||
|  | import Json.Decode as D | ||||||
|  | import Json.Decode.Pipeline as P | ||||||
|  | 
 | ||||||
|  | import Html exposing (..) | ||||||
|  | import Html.Attributes exposing (..) | ||||||
|  | import Html.Events exposing (..) | ||||||
|  | 
 | ||||||
|  | main = Browser.element | ||||||
|  |   { init = \globals -> | ||||||
|  |     ( Context globals <| ItemSearch { searchTerm = "", searchResults = [] } | ||||||
|  |     , Cmd.none | ||||||
|  |     ) | ||||||
|  |   , subscriptions = \_ -> Sub.none | ||||||
|  |   , update = update | ||||||
|  |   , view = view | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | -- Data types | ||||||
|  | 
 | ||||||
|  | type alias SearchResult = | ||||||
|  |   { barcode : String | ||||||
|  |   , name : String | ||||||
|  |   , netUnitPrice : Float | ||||||
|  |   , bought : String | ||||||
|  |   , salesUnits : Int | ||||||
|  |   , available : Bool | ||||||
|  |   , locationName : String | ||||||
|  |   , locationId : Int | ||||||
|  |   , groupName : String | ||||||
|  |   , groupId : Int | ||||||
|  |   , taxGroupId : Int | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | searchResultDecoder = | ||||||
|  |   D.succeed SearchResult | ||||||
|  |     |> P.required "item_barcode" D.string | ||||||
|  |     |> P.required "name" D.string | ||||||
|  |     |> P.required "unit_price" D.float | ||||||
|  |     |> P.required "bought" D.string | ||||||
|  |     |> P.required "sales_units" D.int | ||||||
|  |     |> P.required "available" D.bool | ||||||
|  |     |> P.required "location_name" D.string | ||||||
|  |     |> P.required "location_id" D.int | ||||||
|  |     |> P.required "group_name" D.string | ||||||
|  |     |> P.required "group_id" D.int | ||||||
|  |     |> P.required "tax_group_id" D.int | ||||||
|  | 
 | ||||||
|  | type alias Location = | ||||||
|  |   { id : Int | ||||||
|  |   , name : String | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | type alias Group = | ||||||
|  |   { id : Int | ||||||
|  |   , name : String | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | type alias TaxGroup = | ||||||
|  |   { id : Int | ||||||
|  |   , description : String | ||||||
|  |   , percentage : Float | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | type alias Context = | ||||||
|  |   { globals : Globals | ||||||
|  |   , state : State | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | type alias Globals = | ||||||
|  |   { locations : List Location | ||||||
|  |   , groups : List Group | ||||||
|  |   , taxGroups : List TaxGroup | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | type State | ||||||
|  |   = ItemSearch | ||||||
|  |     { searchTerm : String | ||||||
|  |     , searchResults : List SearchResult | ||||||
|  |     } | ||||||
|  |   | ItemEditor | ||||||
|  |     { barcode : String | ||||||
|  |     , name : String | ||||||
|  |     , salesUnits : Int | ||||||
|  |     , netUnitPrice : Float | ||||||
|  |     , groupId : Int | ||||||
|  |     , locationId : Int | ||||||
|  |     , taxGroupId : Int | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | type Msg | ||||||
|  |   = SetSearchTerm String | ||||||
|  |   | SubmitSearch | ||||||
|  |   | ReceiveSearchResults (Result Http.Error (List SearchResult)) | ||||||
|  |   | GotoItemEditor SearchResult | ||||||
|  |   | SetNetUnitPrice String | ||||||
|  |   | SetGroupId String | ||||||
|  |   | SetLocationId String | ||||||
|  |   | SetTaxGroupId String | ||||||
|  |   | SetBarcode String | ||||||
|  |   | SetName String | ||||||
|  |   | SetSalesUnits String | ||||||
|  | 
 | ||||||
|  | -- Update logic: State machine etc. | ||||||
|  | 
 | ||||||
|  | update msg { globals, state } = | ||||||
|  |   let | ||||||
|  |     (state_, cmd) = updateState msg globals state | ||||||
|  |   in | ||||||
|  |     ({ globals = globals, state = state_ }, cmd) | ||||||
|  | 
 | ||||||
|  | updateState msg globals state = case state of | ||||||
|  |   ItemSearch model -> case msg of | ||||||
|  |     SetSearchTerm searchTerm -> | ||||||
|  |       (ItemSearch { model  | searchTerm = searchTerm }, Cmd.none) | ||||||
|  |     SubmitSearch -> | ||||||
|  |       ( state | ||||||
|  |       , Http.get | ||||||
|  |         { url = "/entry/api/search-items?search-term=" ++ model.searchTerm | ||||||
|  |         , expect = Http.expectJson ReceiveSearchResults <| D.list searchResultDecoder | ||||||
|  |         } | ||||||
|  |       ) | ||||||
|  |     ReceiveSearchResults (Ok searchResults) -> | ||||||
|  |       (ItemSearch { model | searchResults = searchResults }, Cmd.none) | ||||||
|  |     GotoItemEditor searchResult -> | ||||||
|  |       ( ItemEditor | ||||||
|  |         { barcode = searchResult.barcode | ||||||
|  |         , groupId = searchResult.groupId | ||||||
|  |         , locationId = searchResult.locationId | ||||||
|  |         , name = searchResult.name | ||||||
|  |         , netUnitPrice = searchResult.netUnitPrice | ||||||
|  |         , salesUnits = searchResult.salesUnits | ||||||
|  |         , taxGroupId = searchResult.taxGroupId | ||||||
|  |         } | ||||||
|  |       , Cmd.none | ||||||
|  |       ) | ||||||
|  |     _ -> | ||||||
|  |       (state, Cmd.none) | ||||||
|  |   ItemEditor model -> case msg of | ||||||
|  |     SetNetUnitPrice netUnitPriceStr -> case String.toFloat netUnitPriceStr of | ||||||
|  |       Nothing -> (state, Cmd.none) | ||||||
|  |       Just netUnitPrice -> (ItemEditor { model | netUnitPrice = netUnitPrice }, Cmd.none) | ||||||
|  |     SetGroupId groupIdStr -> case String.toInt groupIdStr of | ||||||
|  |       Nothing -> (state, Cmd.none) | ||||||
|  |       Just groupId -> (ItemEditor { model | groupId = groupId }, Cmd.none) | ||||||
|  |     SetLocationId locationIdStr -> case String.toInt locationIdStr of | ||||||
|  |       Nothing -> (state, Cmd.none) | ||||||
|  |       Just locationId -> (ItemEditor { model | locationId = locationId }, Cmd.none) | ||||||
|  |     SetTaxGroupId taxGroupIdStr -> case String.toInt taxGroupIdStr of | ||||||
|  |       Nothing -> (state, Cmd.none) | ||||||
|  |       Just taxGroupId -> (ItemEditor { model | taxGroupId = taxGroupId }, Cmd.none) | ||||||
|  |     SetBarcode barcode -> | ||||||
|  |       (ItemEditor { model | barcode = barcode }, Cmd.none) | ||||||
|  |     SetName name -> | ||||||
|  |       (ItemEditor { model | name = name }, Cmd.none) | ||||||
|  |     SetSalesUnits salesUnitsStr -> case String.toInt salesUnitsStr of | ||||||
|  |       Nothing -> (state, Cmd.none) | ||||||
|  |       Just salesUnits -> (ItemEditor { model | salesUnits = salesUnits }, Cmd.none) | ||||||
|  |     _ -> | ||||||
|  |       (state, Cmd.none) | ||||||
|  | 
 | ||||||
|  | -- View stuff | ||||||
|  | 
 | ||||||
|  | view { globals, state } = case state of | ||||||
|  |   ItemSearch model -> | ||||||
|  |     fieldset [] | ||||||
|  |       [ legend [] [ text "Vorlage für neuen Inventareintrag" ] | ||||||
|  |       , Html.form [ onSubmit SubmitSearch ] | ||||||
|  |         [ div [ class "form-input" ] | ||||||
|  |           [ label [ for "search-term", title "Barcode oder Name" ] [ text "Suchbegriff" ] | ||||||
|  |           , input [ onInput SetSearchTerm, value model.searchTerm, id "search-term" ] [] | ||||||
|  |           ] | ||||||
|  |         , table [] <| searchResultHeaders :: List.map viewSearchResult model.searchResults | ||||||
|  |         ] | ||||||
|  |       ] | ||||||
|  |   ItemEditor model -> case find (\tg -> tg.id == model.taxGroupId) globals.taxGroups of | ||||||
|  |     Nothing -> div [] [ text "index error, this should never happen" ] | ||||||
|  |     Just selectedTaxGroup -> | ||||||
|  |       Html.form [] | ||||||
|  |         [ fieldset [] | ||||||
|  |           [ legend [] [ text "Neuer Inventareintrag" ] | ||||||
|  |           , div [ class "form-input" ] | ||||||
|  |             [ label [ for "barcode" ] [ text "Barcode" ] | ||||||
|  |             , input [ onInput SetBarcode, value model.barcode, disabled True, id "barcode" ] [] | ||||||
|  |             ] | ||||||
|  |           , div [ class "form-input" ] | ||||||
|  |             [ label [ for "name" ] [ text "Name" ] | ||||||
|  |             , input [ onInput SetName, value model.name, id "name" ] [] | ||||||
|  |             ] | ||||||
|  |           , div [ class "form-input" ] | ||||||
|  |             [ label [ for "sales-units" ] [ text "Stückzahl" ] | ||||||
|  |             , input [ onInput SetSalesUnits, value <| String.fromInt model.salesUnits, id "sales-units", type_ "number" ] [] | ||||||
|  |             ] | ||||||
|  |           , div [ class "form-input" ] | ||||||
|  |             [ label [ for "group" ] [ text "Gruppe" ] | ||||||
|  |             , viewSelect | ||||||
|  |               [ onInput SetGroupId, id "group" ] | ||||||
|  |               String.fromInt .id .name model.groupId globals.groups | ||||||
|  |             ] | ||||||
|  |           , div [ class "form-input" ] | ||||||
|  |             [ label [ for "location" ] [ text "Raum" ] | ||||||
|  |             , viewSelect | ||||||
|  |               [ onInput SetLocationId, id "location" ] | ||||||
|  |               String.fromInt .id .name model.locationId globals.locations | ||||||
|  |             ] | ||||||
|  |           , div [ class "form-input" ] | ||||||
|  |             [ label [ for "net-unit-price" ] [ text "Stückpreis (Netto)" ] | ||||||
|  |             , input | ||||||
|  |               [ value <| String.fromFloat model.netUnitPrice | ||||||
|  |               , onInput SetNetUnitPrice | ||||||
|  |               , type_ "number" | ||||||
|  |               , id "net-unit-price" | ||||||
|  |               , step "0.01" | ||||||
|  |               ] | ||||||
|  |               [] | ||||||
|  |             ] | ||||||
|  |           , div [ class "form-input" ] | ||||||
|  |             [ label [ for "tax-group" ] [ text "Steuergruppe" ] | ||||||
|  |             , viewSelect | ||||||
|  |               [ onInput SetTaxGroupId, id "tax-group" ] | ||||||
|  |               String.fromInt .id .description model.taxGroupId globals.taxGroups | ||||||
|  |             ] | ||||||
|  |           ] | ||||||
|  |         , fieldset [] | ||||||
|  |           [ legend [] [ text "Neuer Snackeintrag" ] | ||||||
|  |           , div [ class "form-input" ] | ||||||
|  |             [ label [ for "snack-name" ] [ text "Name" ] | ||||||
|  |             , input [ value model.name, disabled True, id "snack-name" ] [] | ||||||
|  |             ] | ||||||
|  |           , div [ class "form-input" ] | ||||||
|  |             [ label [ for "gross-unit-price" ] | ||||||
|  |               [ text <| "Stückpreis (Brutto), Vorschlag: " ++ String.fromFloat (calculateGarfieldPrice model.netUnitPrice selectedTaxGroup.percentage) | ||||||
|  |               ] | ||||||
|  |             , input [ value <| String.fromFloat <| Maybe.withDefault (calculateGarfieldPrice model.netUnitPrice selectedTaxGroup.percentage) Nothing, id "gross-unit-price", type_ "number", step "0.01" ] [] | ||||||
|  |             ] | ||||||
|  |           ] | ||||||
|  |         ] | ||||||
|  | 
 | ||||||
|  | viewSelect selectAttributes showValue getValue getLabel selectedValue xs = | ||||||
|  |   let | ||||||
|  |     viewOption x = | ||||||
|  |       option | ||||||
|  |         [ value <| showValue <| getValue x | ||||||
|  |         , selected <| getValue x == selectedValue | ||||||
|  |         ] | ||||||
|  |         [ text <| getLabel x | ||||||
|  |         ] | ||||||
|  |   in | ||||||
|  |     select selectAttributes <| List.map viewOption xs | ||||||
|  | 
 | ||||||
|  | searchResultHeaders = | ||||||
|  |   tr [] | ||||||
|  |     [ th [] [ text "Barcode" ] | ||||||
|  |     , th [] [ text "Name" ] | ||||||
|  |     , th [] [ text "Gruppe" ] | ||||||
|  |     , th [] [ text "Stückpreis (Netto)" ] | ||||||
|  |     , th [] [ text "Eingekauft" ] | ||||||
|  |     , th [] [ text "Kaufdatum" ] | ||||||
|  |     , th [] [ text "Raum" ] | ||||||
|  |     , th [] [ text "Aktiv?" ] | ||||||
|  |     , th [] [] | ||||||
|  |     ] | ||||||
|  | 
 | ||||||
|  | viewSearchResult model = | ||||||
|  |   tr [] | ||||||
|  |     [ td [] [ code [] [ text model.barcode ] ] | ||||||
|  |     , td [] [ text model.name ] | ||||||
|  |     , td [] [ text model.groupName ] | ||||||
|  |     , td [] [ text <| String.fromFloat model.netUnitPrice ] | ||||||
|  |     , td [] [ text <| String.fromInt model.salesUnits ] | ||||||
|  |     , td [] [ text model.bought ] | ||||||
|  |     , td [] [ text model.locationName ] | ||||||
|  |     , td [] [ text <| showBool model.available ] | ||||||
|  |     , td [] | ||||||
|  |       [ button [ onClick <| GotoItemEditor model ] [ text "Als Vorlage verwenden" ] | ||||||
|  |       ] | ||||||
|  |     ] | ||||||
|  | 
 | ||||||
|  | calculateGarfieldPrice netUnitPrice taxPercentage = | ||||||
|  |   roundTo 2 <| netUnitPrice * (1 + taxPercentage) + 0.01 | ||||||
|  | 
 | ||||||
|  | roundTo places x = toFloat (round <| x * 10 ^ places) / 10 ^ places | ||||||
|  | 
 | ||||||
|  | showBool b = case b of | ||||||
|  |   True -> "✅" | ||||||
|  |   False -> "❌" | ||||||
|  | 
 | ||||||
|  | find pred xs = List.head <| List.filter pred xs | ||||||
| @ -1,6 +1,6 @@ | |||||||
| SELECT | SELECT | ||||||
|   group_id, |   group_id AS id, | ||||||
|   group_name |   group_name AS name | ||||||
| FROM garfield.inventory_item_groups | FROM garfield.inventory_item_groups | ||||||
| ORDER BY | ORDER BY | ||||||
|   group_name ASC |   group_name ASC | ||||||
							
								
								
									
										4
									
								
								jon/db/entry/get_locations.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								jon/db/entry/get_locations.sql
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,4 @@ | |||||||
|  | SELECT | ||||||
|  |   location_id AS id, | ||||||
|  |   location_name AS name | ||||||
|  | FROM garfield.locations | ||||||
							
								
								
									
										9
									
								
								jon/db/entry/get_tax_groups.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								jon/db/entry/get_tax_groups.sql
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | |||||||
|  | SELECT | ||||||
|  |   tax_group_id AS id, | ||||||
|  |   description, | ||||||
|  |   tax_percentage :: float AS percentage, | ||||||
|  |   tax_description AS description | ||||||
|  | FROM garfield.tax_groups, | ||||||
|  |      LATERAL garfield.tax_find(tax_group_id, NOW() :: date) AS tax_id | ||||||
|  | LEFT JOIN garfield.taxes USING (tax_id) | ||||||
|  | WHERE active | ||||||
							
								
								
									
										18
									
								
								jon/db/search_items.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								jon/db/search_items.sql
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,18 @@ | |||||||
|  | SELECT | ||||||
|  |   item_barcode, | ||||||
|  |   name, | ||||||
|  |   unit_price :: float, | ||||||
|  |   TO_CHAR(bought, 'YYYY-MM-DD') AS bought, | ||||||
|  |   sales_units, | ||||||
|  |   available, | ||||||
|  |   location_name, | ||||||
|  |   location_id, | ||||||
|  |   group_name, | ||||||
|  |   item_group AS group_id, | ||||||
|  |   tax_group AS tax_group_id | ||||||
|  | FROM garfield.inventory_items | ||||||
|  | LEFT JOIN garfield.locations ON location = location_id | ||||||
|  | LEFT JOIN garfield.inventory_item_groups ON item_group = group_id | ||||||
|  | WHERE (%(location_id)s IS NULL OR location = %(location_id)s) | ||||||
|  |   AND (name ILIKE CONCAT('%%', %(search_term)s, '%%') OR item_barcode = %(search_term)s) | ||||||
|  | ORDER BY bought DESC | ||||||
							
								
								
									
										37
									
								
								jon/entry.py
									
									
									
									
									
								
							
							
						
						
									
										37
									
								
								jon/entry.py
									
									
									
									
									
								
							| @ -1,9 +1,6 @@ | |||||||
| import datetime |  | ||||||
| import zoneinfo |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| from flask import Blueprint, redirect, render_template, request, session | from flask import Blueprint, redirect, render_template, request, session | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| from . import db | from . import db | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -12,4 +9,34 @@ bp = Blueprint("entry", __name__, url_prefix="/entry") | |||||||
| 
 | 
 | ||||||
| @bp.get("/") | @bp.get("/") | ||||||
| def index(): | def index(): | ||||||
|     return render_template("entry/index.html") |     with db.run_query("entry/get_groups.sql") as cursor: | ||||||
|  |         groups = cursor.fetchall() | ||||||
|  | 
 | ||||||
|  |     with db.run_query("entry/get_locations.sql") as cursor: | ||||||
|  |         locations = cursor.fetchall() | ||||||
|  | 
 | ||||||
|  |     with db.run_query("entry/get_tax_groups.sql") as cursor: | ||||||
|  |         tax_groups = cursor.fetchall() | ||||||
|  | 
 | ||||||
|  |     return render_template("entry/index.html", **{ | ||||||
|  |         "locations": locations, | ||||||
|  |         "groups": groups, | ||||||
|  |         "tax_groups": tax_groups | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  | @bp.get("/api/search-items") | ||||||
|  | def api_search_items(): | ||||||
|  |     try: | ||||||
|  |         search_term = request.args["search-term"] | ||||||
|  |     except: | ||||||
|  |         return {"error": "Missing query parameter `search-term`"}, 400 | ||||||
|  | 
 | ||||||
|  |     location = session.get("location", None) | ||||||
|  | 
 | ||||||
|  |     with db.run_query("search_items.sql", { | ||||||
|  |         "location_id": None if location is None else location["location_id"], | ||||||
|  |         "search_term": search_term | ||||||
|  |     }) as cursor: | ||||||
|  |         items = cursor.fetchall() | ||||||
|  | 
 | ||||||
|  |     return items | ||||||
|  | |||||||
							
								
								
									
										12011
									
								
								jon/static/entry.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12011
									
								
								jon/static/entry.js
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -1,4 +1,4 @@ | |||||||
| import datetime | import json | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def format_currency(x): | def format_currency(x): | ||||||
| @ -15,16 +15,5 @@ def format_bool(x): | |||||||
|     return "✅" if x else "❌" |     return "✅" if x else "❌" | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def now(): | def to_json(x): | ||||||
|     return datetime.datetime.now() |     return json.dumps(x) | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def get_garfield_price(net_unit_price, tax_group_id): |  | ||||||
|     if tax_group_id == 1: |  | ||||||
|         tax_factor = 1.19 |  | ||||||
|     elif tax_group_id == 2: |  | ||||||
|         tax_factor = 1.07 |  | ||||||
|     else: |  | ||||||
|         raise Error("Unknown tax group ID") |  | ||||||
| 
 |  | ||||||
|     return net_unit_price * tax_factor + 0.01 |  | ||||||
|  | |||||||
| @ -1,5 +1,16 @@ | |||||||
| {% extends "base.html" %} | {% extends "base.html" %} | ||||||
| 
 | 
 | ||||||
| {% block content %} | {% block content %} | ||||||
| <a href="/entry/edit-item-data">Neuer Eintrag</a> | <div class="entry-app"></div> | ||||||
|  | <script src="{{ url_for('static', filename='entry.js') }}"></script> | ||||||
|  | <script> | ||||||
|  | Elm.Entry.init({ | ||||||
|  |   node: document.querySelector('.entry-app'), | ||||||
|  |   flags: { | ||||||
|  |     locations: {{ to_json(locations) | safe }}, | ||||||
|  |     groups: {{ to_json(groups) | safe }}, | ||||||
|  |     taxGroups: {{ to_json(tax_groups) | safe }} | ||||||
|  |   } | ||||||
|  | }); | ||||||
|  | </script> | ||||||
| {% endblock %} | {% endblock %} | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user