From 1c9ef0d8c80d825f3139178aed562200fe05ced1 Mon Sep 17 00:00:00 2001 From: Artsiom Shamsutdzinau Date: Wed, 16 Sep 2020 22:04:18 +0000 Subject: [PATCH 01/10] Add simple calculator to price and amount --- .../live/transaction_live/form_component.ex | 96 +++++++++++++++++++ .../transaction_live/form_component.html.leex | 4 +- 2 files changed, 98 insertions(+), 2 deletions(-) diff --git a/lib/simp_web/live/transaction_live/form_component.ex b/lib/simp_web/live/transaction_live/form_component.ex index b14abeb..5b07d79 100644 --- a/lib/simp_web/live/transaction_live/form_component.ex +++ b/lib/simp_web/live/transaction_live/form_component.ex @@ -43,6 +43,8 @@ defmodule SimpWeb.TransactionLive.FormComponent do @impl true def handle_event("validate", %{"transaction" => transaction_params}, socket) do + transaction_params = parse_price_and_amount(transaction_params) + changeset = socket.assigns.transaction |> Transactions.change_transaction(transaction_params) @@ -68,6 +70,8 @@ defmodule SimpWeb.TransactionLive.FormComponent do } } = socket ) do + transaction_params = parse_price_and_amount(transaction_params) + if current_user.id == transaction.user_id do case Transactions.update_transaction(transaction, transaction_params) do {:ok, _transaction} -> @@ -95,6 +99,8 @@ defmodule SimpWeb.TransactionLive.FormComponent do } } = socket ) do + transaction_params = parse_price_and_amount(transaction_params) + case Transactions.create_transaction(transaction_params, current_user) do {:ok, _transaction} -> {:noreply, @@ -114,4 +120,94 @@ defmodule SimpWeb.TransactionLive.FormComponent do "_new" end end + + def parse_price_and_amount(%{"price" => price, "amount" => amount} = transaction_params) do + price_parsed = parse_price_or_amount(price) + transaction_params = Map.put(transaction_params, "price", price_parsed) + + amount_parsed = parse_price_or_amount(amount) + Map.put(transaction_params, "amount", amount_parsed) + end + + def parse_price_or_amount(string) do + parsingError = %{ + hasError: true, + isDotUsed: false, + sign: nil, + num: "", + res: 0 + } + + %{sign: sign, num: num, res: res, isDotUsed: isDotUsed, hasError: hasError} = + Enum.reduce( + String.graphemes(string), + %{sign: "+", num: "", res: 0, isDotUsed: false, hasError: false}, + fn symbol, %{sign: sign, num: num, res: res, isDotUsed: isDotUsed, hasError: hasError} -> + cond do + hasError or + (Enum.member?(["+", "-"], symbol) and num === "") or + ((symbol === "." and isDotUsed) or + (symbol === "." and num === "")) -> + parsingError + + Enum.member?(["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"], symbol) -> + %{ + hasError: false, + sign: sign, + num: num <> symbol, + res: res, + isDotUsed: isDotUsed + } + + symbol === "." -> + %{ + hasError: false, + sign: sign, + num: num <> symbol, + res: res, + isDotUsed: true + } + + Enum.member?(["+", "-"], symbol) -> + %{ + hasError: false, + sign: symbol, + num: "", + isDotUsed: false, + res: + res + + if sign === "+" do + {parsed, _} = Float.parse(num) + parsed + else + {parsed, _} = Float.parse(num) + -parsed + end + } + + true -> + parsingError + end + end + ) + + cond do + hasError -> + "error" + + true -> + if num === "" do + res + else + res + + if sign === "+" do + {parsed, _} = Float.parse(num) + parsed + else + {parsed, _} = Float.parse(num) + -parsed + end + end + end + end end diff --git a/lib/simp_web/live/transaction_live/form_component.html.leex b/lib/simp_web/live/transaction_live/form_component.html.leex index cf13faa..d8ddb53 100644 --- a/lib/simp_web/live/transaction_live/form_component.html.leex +++ b/lib/simp_web/live/transaction_live/form_component.html.leex @@ -53,13 +53,13 @@
<%= label f, :price %> - <%= number_input f, :price, step: "any" %> + <%= text_input f, :price, inputmode: "tel" %> <%= error_tag f, :price %>
<%= label f, :amount %> - <%= number_input f, :amount, step: "any" %> + <%= text_input f, :amount, inputmode: "tel" %> <%= error_tag f, :amount %>
From 8ec0f9cb290653357c4d053086a581bae52e86b4 Mon Sep 17 00:00:00 2001 From: Artsiom Shamsutdzinau Date: Sun, 11 Oct 2020 07:35:21 +0000 Subject: [PATCH 02/10] Fix searchForm top --- assets/css/components/transactions.scss | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/assets/css/components/transactions.scss b/assets/css/components/transactions.scss index 94eb7bf..03e9157 100644 --- a/assets/css/components/transactions.scss +++ b/assets/css/components/transactions.scss @@ -5,7 +5,7 @@ align-items: center; flex-grow: 1; } - + &__item { max-width: var(--max-page-width); width: 100%; @@ -26,13 +26,13 @@ margin-bottom: 80px; } } - + &__itemSection { display: flex; justify-content: space-between; width: 100%; } - + &__itemLink { position: absolute; width: 100%; @@ -44,7 +44,7 @@ &__searchForm { border-bottom: 1px solid var(--c-gray-4); position: sticky; - top: 46px; + top: 45px; left: 0; background-color: var(--c-gray-1); z-index: 1; From acda4c7e4f473e7d71046ad621e9aa958d3a38c0 Mon Sep 17 00:00:00 2001 From: Artsiom Shamsutdzinau Date: Mon, 9 Nov 2020 18:34:53 +0000 Subject: [PATCH 03/10] Fix searchForm top again --- assets/css/components/transactions.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/css/components/transactions.scss b/assets/css/components/transactions.scss index 03e9157..59a8d00 100644 --- a/assets/css/components/transactions.scss +++ b/assets/css/components/transactions.scss @@ -44,7 +44,7 @@ &__searchForm { border-bottom: 1px solid var(--c-gray-4); position: sticky; - top: 45px; + top: 46px; left: 0; background-color: var(--c-gray-1); z-index: 1; From 80c6c2feafce0c1337c3ae0f06741e52d53dac3e Mon Sep 17 00:00:00 2001 From: Artsiom Shamsutdzinau Date: Sun, 6 Dec 2020 13:09:47 +0300 Subject: [PATCH 04/10] Change reset css --- assets/css/reset.scss | 159 +++++++++++++++++++++++++++++++++--------- 1 file changed, 126 insertions(+), 33 deletions(-) diff --git a/assets/css/reset.scss b/assets/css/reset.scss index afdfb25..345a2c2 100644 --- a/assets/css/reset.scss +++ b/assets/css/reset.scss @@ -1,3 +1,10 @@ +html, +body, +div, +span, +applet, +object, +iframe, h1, h2, h3, @@ -5,66 +12,156 @@ h4, h5, h6, p, -fieldset, -body, -button, +blockquote, +pre, +a, +abbr, +acronym, +address, +big, +cite, +code, +del, +dfn, +em, +img, +ins, +kbd, +q, +s, +samp, +small, +strike, +strong, +sub, +sup, +tt, +var, +b, +u, +i, +center, +dl, +dt, +dd, +ol, ul, +li, +fieldset, +form, +label, +legend, +table, +caption, +tbody, +tfoot, +thead, +tr, +th, +td, +article, +aside, +canvas, +details, +embed, figure, -ol { +figcaption, +footer, +header, +hgroup, +menu, +nav, +output, +ruby, +section, +summary, +time, +mark, +audio, +video { margin: 0; + padding: 0; + border: 0; + font: inherit; + font-size: inherit; + vertical-align: baseline; } -fieldset, -button, -legend, -ul, -ol { - padding: 0; +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +menu, +nav, +section, +img { + display: block; } -fieldset { - border: none; +li { + list-style: none; +} + +blockquote, +q { + quotes: none; +} + +blockquote::before, +blockquote::after, +q::before, +q::after { + content: ""; + content: none; +} + +table { + border-spacing: 0; + border-collapse: collapse; } button { - border: none; width: auto; overflow: visible; + border: none; background-color: transparent; - line-height: inherit; - text-align: inherit; color: inherit; font-family: inherit; font-size: inherit; font-weight: inherit; -} - -ul, -ol { - list-style: none; + line-height: inherit; + text-align: inherit; } input, textarea { + padding: 0; + border: none; + background-color: inherit; + color: inherit; font-family: inherit; - line-height: inherit; font-size: inherit; + line-height: inherit; } -input[type='number'] { +input[type="number"] { appearance: textfield; } -input[type='number']::-webkit-outer-spin-button, -input[type='number']::-webkit-inner-spin-button { +input[type="number"]::-webkit-outer-spin-button, +input[type="number"]::-webkit-inner-spin-button { appearance: none; margin: 0; } -input[type='search']::-webkit-search-decoration, -input[type='search']::-webkit-search-cancel-button, -input[type='search']::-webkit-search-results-button, -input[type='search']::-webkit-search-results-decoration { +input[type="search"]::-webkit-search-decoration, +input[type="search"]::-webkit-search-cancel-button, +input[type="search"]::-webkit-search-results-button, +input[type="search"]::-webkit-search-results-decoration { display: none; } @@ -75,13 +172,9 @@ select { } a { - text-decoration: none; color: inherit; font-family: inherit; font-size: inherit; font-weight: inherit; -} - -img { - display: block; + text-decoration: none; } From 1719607a971c9f788597ba78da23c2ab820f50da Mon Sep 17 00:00:00 2001 From: Artsiom Shamsutdzinau Date: Wed, 30 Dec 2020 13:45:52 +0300 Subject: [PATCH 05/10] change opacity --- assets/css/app.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/css/app.scss b/assets/css/app.scss index 72fa2e5..4897957 100644 --- a/assets/css/app.scss +++ b/assets/css/app.scss @@ -29,7 +29,7 @@ } .phx-click-loading { - opacity: 0.5; + opacity: 0.6; transition: opacity 1s ease-out; } From 095edcdd79ce9f93d6ce9a4ce896ec423031e191 Mon Sep 17 00:00:00 2001 From: Artsiom Shamsutdzinau Date: Sun, 24 Jan 2021 23:57:09 +0300 Subject: [PATCH 06/10] change readme --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ef1cecd..16a3e22 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,25 @@ -# Docker Phoenix +# Simple. Uses Docker Phoenix ## Prerequisites + Install docker and docker-compose. Duplicate `.example.env` file and rename it to `.env` ## Motivation + I wanted to have a starter kit for Phoenix projects that I would be able to run anywhere in docker. Alpine linux is used for all the docker images. If you use vscode - all the necessary extensions for developing phoenix application will be installed automatically inside the container ## Set up for vscode users + 1. Install https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.vscode-remote-extensionpack 2. Open the folder in vscode and it should suggest you to reopen it inside the container 3. After setting up the database you can add connection in postresql explorer (use hostname `db`, user and password from `.env` file) 4. In `devcontainer.json` set `elixir.projectPath` and `elixirLS.projectDir` to the path to your project so elixir-ls extension starts working. ## Get started + 1. Start shell session inside the container. For vscode users - just open the terminal (Ctrl + J) - it will already be inside the container. For others - run e.g. `docker exec -it phoenix-docker_app_1 sh` 2. Create new phoenix project `mix phx.new your_project_name` and press enter to install dependencies when it prompts you 3. To set up postgres open `config/dev.exs`, change hostname to `"db"`, database to `"database"` and run `mix ecto.create` in your project folder From 28fa2fee444ab667efc8b309d60fe82b2ee4873b Mon Sep 17 00:00:00 2001 From: Artsiom Shamsutdzinau Date: Thu, 28 Jan 2021 21:39:09 +0300 Subject: [PATCH 07/10] Switch category on is_expense change, fix parsing --- config/dev.exs | 4 +- .../live/transaction_live/form_component.ex | 46 ++++++++++++++----- 2 files changed, 36 insertions(+), 14 deletions(-) diff --git a/config/dev.exs b/config/dev.exs index 12b631e..376e3af 100644 --- a/config/dev.exs +++ b/config/dev.exs @@ -2,8 +2,8 @@ use Mix.Config # Configure your database config :simp, Simp.Repo, - username: "postgres", - password: "postgres", + username: "postgres_user", + password: "postgres_password", database: "database", hostname: "db", show_sensitive_data_on_connection_error: true, diff --git a/lib/simp_web/live/transaction_live/form_component.ex b/lib/simp_web/live/transaction_live/form_component.ex index 5b07d79..d9b0a15 100644 --- a/lib/simp_web/live/transaction_live/form_component.ex +++ b/lib/simp_web/live/transaction_live/form_component.ex @@ -11,6 +11,7 @@ defmodule SimpWeb.TransactionLive.FormComponent do socket |> assign(assigns) |> assign(:changeset, changeset) + |> assign(:previous_is_expense, Ecto.Changeset.get_field(changeset, :is_expense)) |> set_data()} end @@ -18,16 +19,26 @@ defmodule SimpWeb.TransactionLive.FormComponent do %{ assigns: %{ current_user: current_user, - changeset: changeset + changeset: changeset, + previous_is_expense: previous_is_expense } } = socket ) do + is_expense = Ecto.Changeset.get_field(changeset, :is_expense) + categories = Transactions.list_categories( current_user, - Ecto.Changeset.get_field(changeset, :is_expense) + is_expense ) + changeset = + if previous_is_expense == is_expense do + changeset + else + Ecto.Changeset.change(changeset, category: List.first(categories) || "") + end + names = Transactions.list_names( current_user, @@ -37,7 +48,9 @@ defmodule SimpWeb.TransactionLive.FormComponent do assign(socket, categories: categories, names: names, - currencies: Transactions.list_currencies(current_user) + currencies: Transactions.list_currencies(current_user), + changeset: changeset, + previous_is_expense: is_expense ) end @@ -197,16 +210,25 @@ defmodule SimpWeb.TransactionLive.FormComponent do true -> if num === "" do - res + "" else - res + - if sign === "+" do - {parsed, _} = Float.parse(num) - parsed - else - {parsed, _} = Float.parse(num) - -parsed - end + res = + res + + if sign === "+" do + {parsed, _} = Float.parse(num) + parsed + else + {parsed, _} = Float.parse(num) + -parsed + end + + [integer_part, decimal_part] = String.split(Float.to_string(res), ".") + + if decimal_part == "0" do + String.to_integer(integer_part) + else + res + end end end end From ab8c22bc5bf5be7df724166b1b183231e8901d4b Mon Sep 17 00:00:00 2001 From: Artsiom Shamsutdzinau Date: Mon, 22 Feb 2021 12:07:47 +0300 Subject: [PATCH 08/10] change search top --- assets/css/components/transactions.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/css/components/transactions.scss b/assets/css/components/transactions.scss index 59a8d00..03e9157 100644 --- a/assets/css/components/transactions.scss +++ b/assets/css/components/transactions.scss @@ -44,7 +44,7 @@ &__searchForm { border-bottom: 1px solid var(--c-gray-4); position: sticky; - top: 46px; + top: 45px; left: 0; background-color: var(--c-gray-1); z-index: 1; From 9da56be2978de1f93591de591d05926480d229d9 Mon Sep 17 00:00:00 2001 From: Artsiom Shamsutdzinau Date: Wed, 24 Mar 2021 08:22:35 +0300 Subject: [PATCH 09/10] change input padding --- assets/css/components/input.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/css/components/input.scss b/assets/css/components/input.scss index d8936eb..28fae16 100644 --- a/assets/css/components/input.scss +++ b/assets/css/components/input.scss @@ -4,7 +4,7 @@ input { border: 1px solid var(--c-gray-6); border-radius: var(--bdrs-default); height: 30px; - padding-left: 7px; + padding-left: 6px; outline: none; &:focus { From fb8fa5ebe30bf9ad20d45a19300ead668c2aea62 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 31 Mar 2021 22:35:10 +0000 Subject: [PATCH 10/10] Bump y18n from 3.2.1 to 3.2.2 in /assets Bumps [y18n](https://github.com/yargs/y18n) from 3.2.1 to 3.2.2. - [Release notes](https://github.com/yargs/y18n/releases) - [Changelog](https://github.com/yargs/y18n/blob/master/CHANGELOG.md) - [Commits](https://github.com/yargs/y18n/commits) Signed-off-by: dependabot[bot] --- assets/package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/assets/package-lock.json b/assets/package-lock.json index 4517c82..8bf1ac3 100644 --- a/assets/package-lock.json +++ b/assets/package-lock.json @@ -7925,9 +7925,9 @@ "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" }, "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", + "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", "dev": true }, "yallist": { @@ -7964,9 +7964,9 @@ "dev": true }, "y18n": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz", + "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==", "dev": true } }