Compare commits
36 commits
Author | SHA1 | Date | |
---|---|---|---|
08c8c0f0f7 | |||
1ccab387a9 | |||
92715e9a97 | |||
3deeafd084 | |||
9a41ca7e98 | |||
4ef23a5ec9 | |||
a724826a59 | |||
c60a5e0f12 | |||
223939da6f | |||
d5f7ddf250 | |||
f18fc0ab63 | |||
41c67fe35e | |||
ff9a3a8e92 | |||
053bfed5e6 | |||
b5d52426b6 | |||
65308f8bda | |||
f7f0ebbb25 | |||
7380d3da3c | |||
a5df93e4b7 | |||
bb455f793a | |||
094b0f0324 | |||
9e9d8ffdaf | |||
cf28aa5295 | |||
c254254f57 | |||
9893b278e2 | |||
c7b4373aef | |||
13a1f32d8d | |||
08108ef71a | |||
bc77739425 | |||
eee0b548e9 | |||
6f47a56653 | |||
b98cb3e145 | |||
91c4ca2bc2 | |||
c136d7eb07 | |||
cb4aba5860 | |||
4ec7fc5cf3 |
32 changed files with 1412 additions and 148 deletions
|
@ -19,7 +19,7 @@ jobs:
|
||||||
- name: Setup ssl
|
- name: Setup ssl
|
||||||
run: wget http://archive.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.1f-1ubuntu2.23_amd64.deb && sudo dpkg -i libssl1.1_1.1.1f-1ubuntu2.23_amd64.deb
|
run: wget http://archive.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.1f-1ubuntu2.23_amd64.deb && sudo dpkg -i libssl1.1_1.1.1f-1ubuntu2.23_amd64.deb
|
||||||
|
|
||||||
- uses: erlef/setup-beam@v1
|
- uses: https://github.com/erlef/setup-beam@v1
|
||||||
env:
|
env:
|
||||||
ImageOS: ubuntu24
|
ImageOS: ubuntu24
|
||||||
with:
|
with:
|
|
@ -3,12 +3,12 @@ name: Build and Push Docker Image
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main # Change this to your default branch if different
|
- main
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: Build and Push Image
|
name: Build and Push Image
|
||||||
runs-on: ubuntu-latest-root # This specifies the environment for the job
|
runs-on: ubuntu-latest-root
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout Repository
|
- name: Checkout Repository
|
||||||
|
@ -17,9 +17,9 @@ jobs:
|
||||||
- name: Login to Docker Registry
|
- name: Login to Docker Registry
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: git.vavakado.xyz # Replace with your Docker registry URL
|
registry: git.vavakado.xyz
|
||||||
username: ${{ gitea.actor }} # The actor who triggered the action
|
username: vavakado
|
||||||
password: ${{ secrets.REGISTRY_TOKEN }} # Your registry token secret
|
password: ${{ secrets.REGISTRY_TOKEN }}
|
||||||
|
|
||||||
- name: Build Docker Image
|
- name: Build Docker Image
|
||||||
run: |
|
run: |
|
67
.forgejo/workflows/release.yaml
Normal file
67
.forgejo/workflows/release.yaml
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
name: Manual Release and Docker Image Build
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
version:
|
||||||
|
description: "Version for the release (e.g., v1.0.0)"
|
||||||
|
required: true
|
||||||
|
default: "v1.0.0"
|
||||||
|
type: string
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
name: Create Release and Build Docker Image
|
||||||
|
runs-on: ubuntu-latest-root
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout Repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Login to Docker Registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: git.vavakado.xyz
|
||||||
|
username: vavakado
|
||||||
|
password: ${{ secrets.REGISTRY_TOKEN }}
|
||||||
|
|
||||||
|
- name: Create Git Tag
|
||||||
|
run: |
|
||||||
|
git tag ${{ github.event.inputs.version }}
|
||||||
|
git push origin ${{ github.event.inputs.version }}
|
||||||
|
|
||||||
|
# - name: Create Release
|
||||||
|
# run: |
|
||||||
|
# curl -X POST -H "Authorization: token ${{ secrets.RELEASE_TOKEN }}" \
|
||||||
|
# -H "Content-Type: application/json" \
|
||||||
|
# -d '{
|
||||||
|
# "tag_name": "${{ github.event.inputs.version }}",
|
||||||
|
# "target_commitish": "main", # Change if your default branch is different
|
||||||
|
# "name": "${{ github.event.inputs.version }}",
|
||||||
|
# "body": "Release for version ${{ github.event.inputs.version }}",
|
||||||
|
# "draft": false,
|
||||||
|
# "prerelease": false
|
||||||
|
# }' \
|
||||||
|
# https://git.vavakado.xyz/api/v1/repos/${{ gitea.repository_owner }}/${{ gitea.repository }}/releases
|
||||||
|
|
||||||
|
# - name: Create Release
|
||||||
|
# uses: actions/forgejo-release@v2
|
||||||
|
# with:
|
||||||
|
# direction: upload
|
||||||
|
# tag: ${{ github.event.inputs.version }}
|
||||||
|
# title: ${{ github.event.inputs.version }}
|
||||||
|
# token: ${{ secrets.RELEASE_TOKEN }}
|
||||||
|
# release-notes-assistant: true
|
||||||
|
# release-dir: ./
|
||||||
|
|
||||||
|
- name: Build Docker Image
|
||||||
|
run: |
|
||||||
|
docker build -t git.vavakado.xyz/${{ gitea.repository_owner }}/exmr:${{ github.event.inputs.version }} .
|
||||||
|
docker tag git.vavakado.xyz/vavakado/exmr:${{ github.event.inputs.version }} git.vavakado.xyz/vavakado/exmr:latest
|
||||||
|
docker tag git.vavakado.xyz/vavakado/exmr:${{ github.event.inputs.version }} git.vavakado.xyz/vavakado/exmr:stable
|
||||||
|
|
||||||
|
- name: Push Docker Image
|
||||||
|
run: |
|
||||||
|
docker push git.vavakado.xyz/${{ gitea.repository_owner }}/exmr:${{ github.event.inputs.version }}
|
||||||
|
docker push git.vavakado.xyz/vavakado/exmr:latest
|
||||||
|
docker push git.vavakado.xyz/vavakado/exmr:stable
|
|
@ -18,7 +18,7 @@ ARG DEBIAN_VERSION=bullseye-20241111-slim
|
||||||
ARG BUILDER_IMAGE="hexpm/elixir:${ELIXIR_VERSION}-erlang-${OTP_VERSION}-debian-${DEBIAN_VERSION}"
|
ARG BUILDER_IMAGE="hexpm/elixir:${ELIXIR_VERSION}-erlang-${OTP_VERSION}-debian-${DEBIAN_VERSION}"
|
||||||
ARG RUNNER_IMAGE="debian:${DEBIAN_VERSION}"
|
ARG RUNNER_IMAGE="debian:${DEBIAN_VERSION}"
|
||||||
|
|
||||||
FROM ${BUILDER_IMAGE} as builder
|
FROM ${BUILDER_IMAGE} AS builder
|
||||||
|
|
||||||
# install build dependencies
|
# install build dependencies
|
||||||
RUN apt-get update -y && apt-get install -y build-essential git \
|
RUN apt-get update -y && apt-get install -y build-essential git \
|
||||||
|
|
8
LICENSE
Normal file
8
LICENSE
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
|
||||||
|
Copyright 2024 Vladimir Rubin
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
12
README.md
12
README.md
|
@ -9,12 +9,8 @@ To start your Phoenix server:
|
||||||
|
|
||||||
Now you can visit [`localhost:4000`](http://localhost:4000) from your browser.
|
Now you can visit [`localhost:4000`](http://localhost:4000) from your browser.
|
||||||
|
|
||||||
Ready to run in production? Please [check our deployment guides](https://hexdocs.pm/phoenix/deployment.html).
|
## TODO
|
||||||
|
|
||||||
## Learn more
|
- [ ] Add tests
|
||||||
|
- [ ] Add a proper readme
|
||||||
- Official website: https://www.phoenixframework.org/
|
- [ ] Add a calendar view
|
||||||
- Guides: https://hexdocs.pm/phoenix/overview.html
|
|
||||||
- Docs: https://hexdocs.pm/phoenix
|
|
||||||
- Forum: https://elixirforum.com/c/phoenix-forum
|
|
||||||
- Source: https://github.com/phoenixframework/phoenix
|
|
||||||
|
|
|
@ -16,29 +16,35 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
// Include phoenix_html to handle method=PUT/DELETE in forms and buttons.
|
// Include phoenix_html to handle method=PUT/DELETE in forms and buttons.
|
||||||
import "phoenix_html"
|
import "phoenix_html";
|
||||||
// Establish Phoenix Socket and LiveView configuration.
|
// Establish Phoenix Socket and LiveView configuration.
|
||||||
import {Socket} from "phoenix"
|
import { Socket } from "phoenix";
|
||||||
import {LiveSocket} from "phoenix_live_view"
|
import { LiveSocket } from "phoenix_live_view";
|
||||||
import topbar from "../vendor/topbar"
|
import topbar from "../vendor/topbar";
|
||||||
|
|
||||||
let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content")
|
import darkModeHook from "../vendor/dark_mode";
|
||||||
|
let Hooks = {};
|
||||||
|
Hooks.DarkThemeToggle = darkModeHook;
|
||||||
|
|
||||||
|
let csrfToken = document
|
||||||
|
.querySelector("meta[name='csrf-token']")
|
||||||
|
.getAttribute("content");
|
||||||
let liveSocket = new LiveSocket("/live", Socket, {
|
let liveSocket = new LiveSocket("/live", Socket, {
|
||||||
longPollFallbackMs: 2500,
|
longPollFallbackMs: 2500,
|
||||||
params: {_csrf_token: csrfToken}
|
params: { _csrf_token: csrfToken },
|
||||||
})
|
hooks: Hooks,
|
||||||
|
});
|
||||||
|
|
||||||
// Show progress bar on live navigation and form submits
|
// Show progress bar on live navigation and form submits
|
||||||
topbar.config({barColors: {0: "#29d"}, shadowColor: "rgba(0, 0, 0, .3)"})
|
topbar.config({ barColors: { 0: "#29d" }, shadowColor: "rgba(0, 0, 0, .3)" });
|
||||||
window.addEventListener("phx:page-loading-start", _info => topbar.show(300))
|
window.addEventListener("phx:page-loading-start", (_info) => topbar.show(300));
|
||||||
window.addEventListener("phx:page-loading-stop", _info => topbar.hide())
|
window.addEventListener("phx:page-loading-stop", (_info) => topbar.hide());
|
||||||
|
|
||||||
// connect if there are any LiveViews on the page
|
// connect if there are any LiveViews on the page
|
||||||
liveSocket.connect()
|
liveSocket.connect();
|
||||||
|
|
||||||
// expose liveSocket on window for web console debug logs and latency simulation:
|
// expose liveSocket on window for web console debug logs and latency simulation:
|
||||||
// >> liveSocket.enableDebug()
|
// >> liveSocket.enableDebug()
|
||||||
// >> liveSocket.enableLatencySim(1000) // enabled for duration of browser session
|
// >> liveSocket.enableLatencySim(1000) // enabled for duration of browser session
|
||||||
// >> liveSocket.disableLatencySim()
|
// >> liveSocket.disableLatencySim()
|
||||||
window.liveSocket = liveSocket
|
window.liveSocket = liveSocket;
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ const path = require("path");
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
content: ["./js/**/*.js", "../lib/exmr_web.ex", "../lib/exmr_web/**/*.*ex"],
|
content: ["./js/**/*.js", "../lib/exmr_web.ex", "../lib/exmr_web/**/*.*ex"],
|
||||||
|
darkMode: "class",
|
||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
colors: {
|
colors: {
|
||||||
|
|
46
assets/vendor/dark_mode.js
vendored
Normal file
46
assets/vendor/dark_mode.js
vendored
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
const localStorageKey = "theme";
|
||||||
|
|
||||||
|
const isDark = () => {
|
||||||
|
if (localStorage.getItem(localStorageKey) === "dark") return true;
|
||||||
|
if (localStorage.getItem(localStorageKey) === "light") return false;
|
||||||
|
return window.matchMedia("(prefers-color-scheme: dark)").matches;
|
||||||
|
};
|
||||||
|
|
||||||
|
const setupThemeToggle = () => {
|
||||||
|
toggleVisibility = (dark) => {
|
||||||
|
const themeToggleDarkIcon = document.getElementById(
|
||||||
|
"theme-toggle-dark-icon",
|
||||||
|
);
|
||||||
|
const themeToggleLightIcon = document.getElementById(
|
||||||
|
"theme-toggle-light-icon",
|
||||||
|
);
|
||||||
|
if (themeToggleDarkIcon == null || themeToggleLightIcon == null) return;
|
||||||
|
const show = dark ? themeToggleDarkIcon : themeToggleLightIcon;
|
||||||
|
const hide = dark ? themeToggleLightIcon : themeToggleDarkIcon;
|
||||||
|
show.classList.remove("hidden", "text-transparent");
|
||||||
|
hide.classList.add("hidden", "text-transparent");
|
||||||
|
if (dark) {
|
||||||
|
document.documentElement.classList.add("dark");
|
||||||
|
} else {
|
||||||
|
document.documentElement.classList.remove("dark");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
localStorage.setItem(localStorageKey, dark ? "dark" : "light");
|
||||||
|
} catch (_err) { }
|
||||||
|
};
|
||||||
|
toggleVisibility(isDark());
|
||||||
|
document
|
||||||
|
.getElementById("theme-toggle")
|
||||||
|
.addEventListener("click", function() {
|
||||||
|
toggleVisibility(!isDark());
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const darkModeHook = {
|
||||||
|
mounted() {
|
||||||
|
setupThemeToggle();
|
||||||
|
},
|
||||||
|
updated() { },
|
||||||
|
};
|
||||||
|
|
||||||
|
export default darkModeHook;
|
|
@ -48,9 +48,13 @@ defmodule ExmrWeb.CoreComponents do
|
||||||
phx-mounted={@show && show_modal(@id)}
|
phx-mounted={@show && show_modal(@id)}
|
||||||
phx-remove={hide_modal(@id)}
|
phx-remove={hide_modal(@id)}
|
||||||
data-cancel={JS.exec(@on_cancel, "phx-remove")}
|
data-cancel={JS.exec(@on_cancel, "phx-remove")}
|
||||||
class="relative z-50 hidden"
|
class="relative z-50 hidden bg-zinc-50/90 dark:bg-zinc-950"
|
||||||
>
|
>
|
||||||
<div id={"#{@id}-bg"} class="bg-zinc-50/90 fixed inset-0 transition-opacity" aria-hidden="true" />
|
<div
|
||||||
|
id={"#{@id}-bg"}
|
||||||
|
class="bg-zinc-50/90 dark:bg-zinc-800/90 fixed inset-0 transition-opacity"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
<div
|
<div
|
||||||
class="fixed inset-0 overflow-y-auto"
|
class="fixed inset-0 overflow-y-auto"
|
||||||
aria-labelledby={"#{@id}-title"}
|
aria-labelledby={"#{@id}-title"}
|
||||||
|
@ -66,7 +70,7 @@ defmodule ExmrWeb.CoreComponents do
|
||||||
phx-window-keydown={JS.exec("data-cancel", to: "##{@id}")}
|
phx-window-keydown={JS.exec("data-cancel", to: "##{@id}")}
|
||||||
phx-key="escape"
|
phx-key="escape"
|
||||||
phx-click-away={JS.exec("data-cancel", to: "##{@id}")}
|
phx-click-away={JS.exec("data-cancel", to: "##{@id}")}
|
||||||
class="shadow-zinc-700/10 ring-zinc-700/10 relative hidden rounded-2xl bg-white p-14 shadow-lg ring-1 transition"
|
class="shadow-zinc-700/10 ring-zinc-700/10 relative hidden rounded-2xl bg-white dark:bg-zinc-950 p-14 shadow-lg ring-1 transition"
|
||||||
>
|
>
|
||||||
<div class="absolute top-6 right-5">
|
<div class="absolute top-6 right-5">
|
||||||
<button
|
<button
|
||||||
|
@ -79,7 +83,7 @@ defmodule ExmrWeb.CoreComponents do
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div id={"#{@id}-content"}>
|
<div id={"#{@id}-content"}>
|
||||||
<%= render_slot(@inner_block) %>
|
{render_slot(@inner_block)}
|
||||||
</div>
|
</div>
|
||||||
</.focus_wrap>
|
</.focus_wrap>
|
||||||
</div>
|
</div>
|
||||||
|
@ -124,9 +128,9 @@ defmodule ExmrWeb.CoreComponents do
|
||||||
<p :if={@title} class="flex items-center gap-1.5 text-sm font-semibold leading-6">
|
<p :if={@title} class="flex items-center gap-1.5 text-sm font-semibold leading-6">
|
||||||
<.icon :if={@kind == :info} name="hero-information-circle-mini" class="h-4 w-4" />
|
<.icon :if={@kind == :info} name="hero-information-circle-mini" class="h-4 w-4" />
|
||||||
<.icon :if={@kind == :error} name="hero-exclamation-circle-mini" class="h-4 w-4" />
|
<.icon :if={@kind == :error} name="hero-exclamation-circle-mini" class="h-4 w-4" />
|
||||||
<%= @title %>
|
{@title}
|
||||||
</p>
|
</p>
|
||||||
<p class="mt-2 text-sm leading-5"><%= msg %></p>
|
<p class="mt-2 text-sm leading-5">{msg}</p>
|
||||||
<button type="button" class="group absolute top-1 right-1 p-2" aria-label={gettext("close")}>
|
<button type="button" class="group absolute top-1 right-1 p-2" aria-label={gettext("close")}>
|
||||||
<.icon name="hero-x-mark-solid" class="h-5 w-5 opacity-40 group-hover:opacity-70" />
|
<.icon name="hero-x-mark-solid" class="h-5 w-5 opacity-40 group-hover:opacity-70" />
|
||||||
</button>
|
</button>
|
||||||
|
@ -157,7 +161,7 @@ defmodule ExmrWeb.CoreComponents do
|
||||||
phx-connected={hide("#client-error")}
|
phx-connected={hide("#client-error")}
|
||||||
hidden
|
hidden
|
||||||
>
|
>
|
||||||
<%= gettext("Attempting to reconnect") %>
|
{gettext("Attempting to reconnect")}
|
||||||
<.icon name="hero-arrow-path" class="ml-1 h-3 w-3 animate-spin" />
|
<.icon name="hero-arrow-path" class="ml-1 h-3 w-3 animate-spin" />
|
||||||
</.flash>
|
</.flash>
|
||||||
|
|
||||||
|
@ -169,7 +173,7 @@ defmodule ExmrWeb.CoreComponents do
|
||||||
phx-connected={hide("#server-error")}
|
phx-connected={hide("#server-error")}
|
||||||
hidden
|
hidden
|
||||||
>
|
>
|
||||||
<%= gettext("Hang in there while we get back on track") %>
|
{gettext("Hang in there while we get back on track")}
|
||||||
<.icon name="hero-arrow-path" class="ml-1 h-3 w-3 animate-spin" />
|
<.icon name="hero-arrow-path" class="ml-1 h-3 w-3 animate-spin" />
|
||||||
</.flash>
|
</.flash>
|
||||||
</div>
|
</div>
|
||||||
|
@ -202,10 +206,13 @@ defmodule ExmrWeb.CoreComponents do
|
||||||
def simple_form(assigns) do
|
def simple_form(assigns) do
|
||||||
~H"""
|
~H"""
|
||||||
<.form :let={f} for={@for} as={@as} {@rest}>
|
<.form :let={f} for={@for} as={@as} {@rest}>
|
||||||
<div class="mt-10 space-y-8 bg-white">
|
<div class="mt-10 space-y-8 bg-white dark:bg-zinc-950">
|
||||||
<%= render_slot(@inner_block, f) %>
|
{render_slot(@inner_block, f)}
|
||||||
<div :for={action <- @actions} class="mt-2 flex items-center justify-between gap-6">
|
<div
|
||||||
<%= render_slot(action, f) %>
|
:for={action <- @actions}
|
||||||
|
class="mt-2 flex items-center justify-between gap-6 dark:text-zinc-100"
|
||||||
|
>
|
||||||
|
{render_slot(action, f)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</.form>
|
</.form>
|
||||||
|
@ -232,12 +239,12 @@ defmodule ExmrWeb.CoreComponents do
|
||||||
type={@type}
|
type={@type}
|
||||||
class={[
|
class={[
|
||||||
"phx-submit-loading:opacity-75 rounded-lg bg-zinc-900 hover:bg-zinc-700 py-2 px-3",
|
"phx-submit-loading:opacity-75 rounded-lg bg-zinc-900 hover:bg-zinc-700 py-2 px-3",
|
||||||
"text-sm font-semibold leading-6 text-white active:text-white/80",
|
"text-sm font-semibold leading-6",
|
||||||
@class
|
@class
|
||||||
]}
|
]}
|
||||||
{@rest}
|
{@rest}
|
||||||
>
|
>
|
||||||
<%= render_slot(@inner_block) %>
|
{render_slot(@inner_block)}
|
||||||
</button>
|
</button>
|
||||||
"""
|
"""
|
||||||
end
|
end
|
||||||
|
@ -310,7 +317,7 @@ defmodule ExmrWeb.CoreComponents do
|
||||||
|
|
||||||
~H"""
|
~H"""
|
||||||
<div>
|
<div>
|
||||||
<label class="flex items-center gap-4 text-sm leading-6 text-zinc-600">
|
<label class="flex items-center gap-4 text-sm leading-6 text-zinc-600 dark:text-zinc-200">
|
||||||
<input type="hidden" name={@name} value="false" disabled={@rest[:disabled]} />
|
<input type="hidden" name={@name} value="false" disabled={@rest[:disabled]} />
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
|
@ -321,9 +328,9 @@ defmodule ExmrWeb.CoreComponents do
|
||||||
class="rounded border-zinc-300 text-zinc-900 focus:ring-0"
|
class="rounded border-zinc-300 text-zinc-900 focus:ring-0"
|
||||||
{@rest}
|
{@rest}
|
||||||
/>
|
/>
|
||||||
<%= @label %>
|
{@label}
|
||||||
</label>
|
</label>
|
||||||
<.error :for={msg <- @errors}><%= msg %></.error>
|
<.error :for={msg <- @errors}>{msg}</.error>
|
||||||
</div>
|
</div>
|
||||||
"""
|
"""
|
||||||
end
|
end
|
||||||
|
@ -331,7 +338,7 @@ defmodule ExmrWeb.CoreComponents do
|
||||||
def input(%{type: "select"} = assigns) do
|
def input(%{type: "select"} = assigns) do
|
||||||
~H"""
|
~H"""
|
||||||
<div>
|
<div>
|
||||||
<.label for={@id}><%= @label %></.label>
|
<.label for={@id}>{@label}</.label>
|
||||||
<select
|
<select
|
||||||
id={@id}
|
id={@id}
|
||||||
name={@name}
|
name={@name}
|
||||||
|
@ -339,10 +346,10 @@ defmodule ExmrWeb.CoreComponents do
|
||||||
multiple={@multiple}
|
multiple={@multiple}
|
||||||
{@rest}
|
{@rest}
|
||||||
>
|
>
|
||||||
<option :if={@prompt} value=""><%= @prompt %></option>
|
<option :if={@prompt} value="">{@prompt}</option>
|
||||||
<%= Phoenix.HTML.Form.options_for_select(@options, @value) %>
|
{Phoenix.HTML.Form.options_for_select(@options, @value)}
|
||||||
</select>
|
</select>
|
||||||
<.error :for={msg <- @errors}><%= msg %></.error>
|
<.error :for={msg <- @errors}>{msg}</.error>
|
||||||
</div>
|
</div>
|
||||||
"""
|
"""
|
||||||
end
|
end
|
||||||
|
@ -350,7 +357,7 @@ defmodule ExmrWeb.CoreComponents do
|
||||||
def input(%{type: "textarea"} = assigns) do
|
def input(%{type: "textarea"} = assigns) do
|
||||||
~H"""
|
~H"""
|
||||||
<div>
|
<div>
|
||||||
<.label for={@id}><%= @label %></.label>
|
<.label for={@id}>{@label}</.label>
|
||||||
<textarea
|
<textarea
|
||||||
id={@id}
|
id={@id}
|
||||||
name={@name}
|
name={@name}
|
||||||
|
@ -361,7 +368,7 @@ defmodule ExmrWeb.CoreComponents do
|
||||||
]}
|
]}
|
||||||
{@rest}
|
{@rest}
|
||||||
><%= Phoenix.HTML.Form.normalize_value("textarea", @value) %></textarea>
|
><%= Phoenix.HTML.Form.normalize_value("textarea", @value) %></textarea>
|
||||||
<.error :for={msg <- @errors}><%= msg %></.error>
|
<.error :for={msg <- @errors}>{msg}</.error>
|
||||||
</div>
|
</div>
|
||||||
"""
|
"""
|
||||||
end
|
end
|
||||||
|
@ -370,7 +377,7 @@ defmodule ExmrWeb.CoreComponents do
|
||||||
def input(assigns) do
|
def input(assigns) do
|
||||||
~H"""
|
~H"""
|
||||||
<div>
|
<div>
|
||||||
<.label for={@id}><%= @label %></.label>
|
<.label for={@id}>{@label}</.label>
|
||||||
<input
|
<input
|
||||||
type={@type}
|
type={@type}
|
||||||
name={@name}
|
name={@name}
|
||||||
|
@ -383,7 +390,7 @@ defmodule ExmrWeb.CoreComponents do
|
||||||
]}
|
]}
|
||||||
{@rest}
|
{@rest}
|
||||||
/>
|
/>
|
||||||
<.error :for={msg <- @errors}><%= msg %></.error>
|
<.error :for={msg <- @errors}>{msg}</.error>
|
||||||
</div>
|
</div>
|
||||||
"""
|
"""
|
||||||
end
|
end
|
||||||
|
@ -396,8 +403,8 @@ defmodule ExmrWeb.CoreComponents do
|
||||||
|
|
||||||
def label(assigns) do
|
def label(assigns) do
|
||||||
~H"""
|
~H"""
|
||||||
<label for={@for} class="block text-sm font-semibold leading-6 text-zinc-800">
|
<label for={@for} class="block text-sm font-semibold leading-6 text-zinc-800 dark:text-zinc-200">
|
||||||
<%= render_slot(@inner_block) %>
|
{render_slot(@inner_block)}
|
||||||
</label>
|
</label>
|
||||||
"""
|
"""
|
||||||
end
|
end
|
||||||
|
@ -411,7 +418,7 @@ defmodule ExmrWeb.CoreComponents do
|
||||||
~H"""
|
~H"""
|
||||||
<p class="mt-3 flex gap-3 text-sm leading-6 text-rose-600">
|
<p class="mt-3 flex gap-3 text-sm leading-6 text-rose-600">
|
||||||
<.icon name="hero-exclamation-circle-mini" class="mt-0.5 h-5 w-5 flex-none" />
|
<.icon name="hero-exclamation-circle-mini" class="mt-0.5 h-5 w-5 flex-none" />
|
||||||
<%= render_slot(@inner_block) %>
|
{render_slot(@inner_block)}
|
||||||
</p>
|
</p>
|
||||||
"""
|
"""
|
||||||
end
|
end
|
||||||
|
@ -429,14 +436,14 @@ defmodule ExmrWeb.CoreComponents do
|
||||||
~H"""
|
~H"""
|
||||||
<header class={[@actions != [] && "flex items-center justify-between gap-6", @class]}>
|
<header class={[@actions != [] && "flex items-center justify-between gap-6", @class]}>
|
||||||
<div>
|
<div>
|
||||||
<h1 class="text-lg font-semibold leading-8 text-zinc-800">
|
<h1 class="text-lg font-semibold leading-none text-zinc-800 dark:text-zinc-200">
|
||||||
<%= render_slot(@inner_block) %>
|
{render_slot(@inner_block)}
|
||||||
</h1>
|
</h1>
|
||||||
<p :if={@subtitle != []} class="mt-2 text-sm leading-6 text-zinc-600">
|
<p :if={@subtitle != []} class="mt-2 text-sm leading-6 text-zinc-600 dark:text-zinc-100">
|
||||||
<%= render_slot(@subtitle) %>
|
{render_slot(@subtitle)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-none"><%= render_slot(@actions) %></div>
|
<div class="flex-none">{render_slot(@actions)}</div>
|
||||||
</header>
|
</header>
|
||||||
"""
|
"""
|
||||||
end
|
end
|
||||||
|
@ -477,9 +484,9 @@ defmodule ExmrWeb.CoreComponents do
|
||||||
<table class="w-[40rem] mt-11 sm:w-full">
|
<table class="w-[40rem] mt-11 sm:w-full">
|
||||||
<thead class="text-sm text-left leading-6 text-zinc-500">
|
<thead class="text-sm text-left leading-6 text-zinc-500">
|
||||||
<tr>
|
<tr>
|
||||||
<th :for={col <- @col} class="p-0 pb-4 pr-6 font-normal"><%= col[:label] %></th>
|
<th :for={col <- @col} class="p-0 pb-4 pr-6 font-normal">{col[:label]}</th>
|
||||||
<th :if={@action != []} class="relative p-0 pb-4">
|
<th :if={@action != []} class="relative p-0 pb-4">
|
||||||
<span class="sr-only"><%= gettext("Actions") %></span>
|
<span class="sr-only">{gettext("Actions")}</span>
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
@ -497,7 +504,7 @@ defmodule ExmrWeb.CoreComponents do
|
||||||
<div class="block py-4 pr-6">
|
<div class="block py-4 pr-6">
|
||||||
<span class="absolute -inset-y-px right-0 -left-4 group-hover:bg-zinc-50 sm:rounded-l-xl" />
|
<span class="absolute -inset-y-px right-0 -left-4 group-hover:bg-zinc-50 sm:rounded-l-xl" />
|
||||||
<span class={["relative", i == 0 && "font-semibold text-zinc-900"]}>
|
<span class={["relative", i == 0 && "font-semibold text-zinc-900"]}>
|
||||||
<%= render_slot(col, @row_item.(row)) %>
|
{render_slot(col, @row_item.(row))}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
@ -508,7 +515,7 @@ defmodule ExmrWeb.CoreComponents do
|
||||||
:for={action <- @action}
|
:for={action <- @action}
|
||||||
class="relative ml-4 font-semibold leading-6 text-zinc-900 hover:text-zinc-700"
|
class="relative ml-4 font-semibold leading-6 text-zinc-900 hover:text-zinc-700"
|
||||||
>
|
>
|
||||||
<%= render_slot(action, @row_item.(row)) %>
|
{render_slot(action, @row_item.(row))}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
@ -538,8 +545,8 @@ defmodule ExmrWeb.CoreComponents do
|
||||||
<div class="mt-14">
|
<div class="mt-14">
|
||||||
<dl class="-my-4 divide-y divide-zinc-100">
|
<dl class="-my-4 divide-y divide-zinc-100">
|
||||||
<div :for={item <- @item} class="flex gap-4 py-4 text-sm leading-6 sm:gap-8">
|
<div :for={item <- @item} class="flex gap-4 py-4 text-sm leading-6 sm:gap-8">
|
||||||
<dt class="w-1/4 flex-none text-zinc-500"><%= item.title %></dt>
|
<dt class="w-1/4 flex-none text-zinc-500">{item.title}</dt>
|
||||||
<dd class="text-zinc-700"><%= render_slot(item) %></dd>
|
<dd class="text-zinc-700">{render_slot(item)}</dd>
|
||||||
</div>
|
</div>
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
|
@ -564,7 +571,7 @@ defmodule ExmrWeb.CoreComponents do
|
||||||
class="text-sm font-semibold leading-6 text-zinc-900 hover:text-zinc-700"
|
class="text-sm font-semibold leading-6 text-zinc-900 hover:text-zinc-700"
|
||||||
>
|
>
|
||||||
<.icon name="hero-arrow-left-solid" class="h-3 w-3" />
|
<.icon name="hero-arrow-left-solid" class="h-3 w-3" />
|
||||||
<%= render_slot(@inner_block) %>
|
{render_slot(@inner_block)}
|
||||||
</.link>
|
</.link>
|
||||||
</div>
|
</div>
|
||||||
"""
|
"""
|
||||||
|
|
57
lib/exmr_web/components/dark_mode.ex
Normal file
57
lib/exmr_web/components/dark_mode.ex
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
defmodule DarkMode do
|
||||||
|
@moduledoc """
|
||||||
|
A component to toggle dark mode.
|
||||||
|
"""
|
||||||
|
|
||||||
|
use Phoenix.Component
|
||||||
|
|
||||||
|
def button(assigns) do
|
||||||
|
~H"""
|
||||||
|
<button
|
||||||
|
id="theme-toggle"
|
||||||
|
type="button"
|
||||||
|
phx-update="ignore"
|
||||||
|
phx-hook="DarkThemeToggle"
|
||||||
|
class="text-zinc-500 dark:text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-700 rounded-lg text-sm p-2"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
id="theme-toggle-dark-icon"
|
||||||
|
class="w-5 h-5 text-transparent hidden"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z"></path>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
<svg
|
||||||
|
id="theme-toggle-light-icon"
|
||||||
|
class="w-5 h-5 text-transparent"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
>
|
||||||
|
</path>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Toggle early based on <html class="dark">
|
||||||
|
const themeToggleDarkIcon = document.getElementById('theme-toggle-dark-icon');
|
||||||
|
const themeToggleLightIcon = document.getElementById('theme-toggle-light-icon');
|
||||||
|
if (themeToggleDarkIcon != null && themeToggleLightIcon != null) {
|
||||||
|
let dark = document.documentElement.classList.contains('dark');
|
||||||
|
const show = dark ? themeToggleDarkIcon : themeToggleLightIcon
|
||||||
|
const hide = dark ? themeToggleLightIcon : themeToggleDarkIcon
|
||||||
|
show.classList.remove('hidden', 'text-transparent');
|
||||||
|
hide.classList.add('hidden', 'text-transparent');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
"""
|
||||||
|
end
|
||||||
|
end
|
|
@ -5,7 +5,7 @@
|
||||||
<img src={~p"/images/logo.svg"} width="36" />
|
<img src={~p"/images/logo.svg"} width="36" />
|
||||||
</a>
|
</a>
|
||||||
<p class="bg-brand/5 text-brand rounded-full px-2 font-medium leading-6">
|
<p class="bg-brand/5 text-brand rounded-full px-2 font-medium leading-6">
|
||||||
v<%= Application.spec(:exmr, :vsn) %>
|
v{Application.spec(:exmr, :vsn)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -13,6 +13,6 @@
|
||||||
<main class="px-4 py-20 sm:px-6 lg:px-8">
|
<main class="px-4 py-20 sm:px-6 lg:px-8">
|
||||||
<div class="mx-auto max-w-2xl">
|
<div class="mx-auto max-w-2xl">
|
||||||
<.flash_group flash={@flash} />
|
<.flash_group flash={@flash} />
|
||||||
<%= @inner_content %>
|
{@inner_content}
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
|
@ -4,55 +4,63 @@
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<meta name="csrf-token" content={get_csrf_token()} />
|
<meta name="csrf-token" content={get_csrf_token()} />
|
||||||
<.live_title suffix=" · Phoenix Framework">
|
<.live_title>
|
||||||
<%= assigns[:page_title] || "Exmr" %>
|
{assigns[:page_title] || "ExMR"}
|
||||||
</.live_title>
|
</.live_title>
|
||||||
<link phx-track-static rel="stylesheet" href={~p"/assets/app.css"} />
|
<link phx-track-static rel="stylesheet" href={~p"/assets/app.css"} />
|
||||||
|
<script>
|
||||||
|
if (localStorage.getItem('theme') === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
|
||||||
|
document.documentElement.classList.add('dark');
|
||||||
|
} else {
|
||||||
|
document.documentElement.classList.remove('dark')
|
||||||
|
}
|
||||||
|
</script>
|
||||||
<script defer phx-track-static type="text/javascript" src={~p"/assets/app.js"}>
|
<script defer phx-track-static type="text/javascript" src={~p"/assets/app.js"}>
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body class="bg-white">
|
<body class="bg-white dark:bg-zinc-950 dark:text-zinc-100">
|
||||||
<ul class="relative z-10 flex items-center gap-4 px-4 sm:px-6 lg:px-8 justify-end">
|
<ul class="relative z-10 flex items-center gap-4 px-4 sm:px-6 lg:px-8 justify-end">
|
||||||
|
<DarkMode.button />
|
||||||
<%= if @current_user do %>
|
<%= if @current_user do %>
|
||||||
<li class="text-[0.8125rem] leading-6 text-zinc-900">
|
<li class="text-[0.8125rem] leading-6 text-zinc-900 dark:text-zinc-300">
|
||||||
<%= @current_user.email %>
|
{@current_user.email}
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<.link
|
<.link
|
||||||
href={~p"/users/settings"}
|
href={~p"/users/settings"}
|
||||||
class="text-[0.8125rem] leading-6 text-zinc-900 font-semibold hover:text-zinc-700"
|
class="text-[0.8125rem] leading-6 text-zinc-900 font-semibold hover:text-zinc-700 dark:text-zinc-300 dark:hover:text-zinc-100"
|
||||||
>
|
>
|
||||||
Settings
|
{gettext("Settings")}
|
||||||
</.link>
|
</.link>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<.link
|
<.link
|
||||||
href={~p"/users/log_out"}
|
href={~p"/users/log_out"}
|
||||||
method="delete"
|
method="delete"
|
||||||
class="text-[0.8125rem] leading-6 text-zinc-900 font-semibold hover:text-zinc-700"
|
class="text-[0.8125rem] leading-6 text-zinc-900 font-semibold hover:text-zinc-700 dark:text-zinc-300 dark:hover:text-zinc-100"
|
||||||
>
|
>
|
||||||
Log out
|
{gettext("Log out")}
|
||||||
</.link>
|
</.link>
|
||||||
</li>
|
</li>
|
||||||
<% else %>
|
<% else %>
|
||||||
<li>
|
<li>
|
||||||
<.link
|
<.link
|
||||||
href={~p"/users/register"}
|
href={~p"/users/register"}
|
||||||
class="text-[0.8125rem] leading-6 text-zinc-900 font-semibold hover:text-zinc-700"
|
class="text-[0.8125rem] leading-6 text-zinc-900 font-semibold hover:text-zinc-700 dark:text-zinc-300 dark:hover:text-zinc-100"
|
||||||
>
|
>
|
||||||
Register
|
{gettext("Register")}
|
||||||
</.link>
|
</.link>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<.link
|
<.link
|
||||||
href={~p"/users/log_in"}
|
href={~p"/users/log_in"}
|
||||||
class="text-[0.8125rem] leading-6 text-zinc-900 font-semibold hover:text-zinc-700"
|
class="text-[0.8125rem] leading-6 text-zinc-900 font-semibold hover:text-zinc-700 dark:text-zinc-300 dark:hover:text-zinc-100"
|
||||||
>
|
>
|
||||||
Log in
|
{gettext("Log in")}
|
||||||
</.link>
|
</.link>
|
||||||
</li>
|
</li>
|
||||||
<% end %>
|
<% end %>
|
||||||
</ul>
|
</ul>
|
||||||
<%= @inner_content %>
|
{@inner_content}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -44,25 +44,25 @@
|
||||||
<h1 class="mt-10 flex items-center text-sm font-semibold leading-6 text-purple-400">
|
<h1 class="mt-10 flex items-center text-sm font-semibold leading-6 text-purple-400">
|
||||||
ExMR
|
ExMR
|
||||||
<small class="bg-brand/5 text-[0.8125rem] ml-3 rounded-full px-2 font-medium leading-6 text-purple-400">
|
<small class="bg-brand/5 text-[0.8125rem] ml-3 rounded-full px-2 font-medium leading-6 text-purple-400">
|
||||||
v<%= Application.spec(:exmr, :vsn) %>
|
v{Application.spec(:exmr, :vsn)}
|
||||||
</small>
|
</small>
|
||||||
</h1>
|
</h1>
|
||||||
<p class="text-[2rem] mt-4 font-semibold leading-10 tracking-tighter text-zinc-900 text-balance">
|
<p class="text-[2rem] mt-4 font-semibold leading-10 tracking-tighter text-zinc-900 dark:text-zinc-200/90 text-balance">
|
||||||
hell yeah
|
{gettext("A simple, modern, and fast exam management system.")}
|
||||||
</p>
|
</p>
|
||||||
<p class="mt-4 text-base leading-7 text-zinc-600">
|
<p class="mt-4 text-base leading-7 text-zinc-600 dark:text-zinc-300">
|
||||||
y'all really though i'm gonna be serious? lol
|
{gettext("Built using Phoenix LiveView, Ecto, and TailwindCSS by @vavakado")}
|
||||||
</p>
|
</p>
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<div class="w-full sm:w-auto">
|
<div class="w-full sm:w-auto">
|
||||||
<div class="mt-10 grid grid-cols-1 gap-x-6 gap-y-4 sm:grid-cols-3">
|
<div class="mt-10 grid grid-cols-1 gap-x-6 gap-y-4 sm:grid-cols-3">
|
||||||
<a
|
<a
|
||||||
href="https://github.com/phoenixframework/phoenix"
|
href="https://git.vavakado.xyz/vavakado/exmr"
|
||||||
class="group relative rounded-2xl px-6 py-4 text-sm font-semibold leading-6 text-zinc-900 sm:py-6"
|
class="group relative rounded-2xl px-6 py-4 text-sm font-semibold leading-6 text-zinc-900 sm:py-6"
|
||||||
>
|
>
|
||||||
<span class="absolute inset-0 rounded-2xl bg-zinc-50 transition group-hover:bg-zinc-100 sm:group-hover:scale-105">
|
<span class="absolute inset-0 rounded-2xl bg-zinc-50 transition group-hover:bg-zinc-100 dark:bg-zinc-800 dark:group-hover:bg-zinc-900/95 sm:group-hover:scale-105">
|
||||||
</span>
|
</span>
|
||||||
<span class="relative flex items-center gap-4 sm:flex-col">
|
<span class="relative flex items-center gap-4 sm:flex-col dark:text-white">
|
||||||
<svg viewBox="0 0 24 24" aria-hidden="true" class="h-6 w-6">
|
<svg viewBox="0 0 24 24" aria-hidden="true" class="h-6 w-6">
|
||||||
<path
|
<path
|
||||||
fill-rule="evenodd"
|
fill-rule="evenodd"
|
||||||
|
@ -79,9 +79,9 @@
|
||||||
href="https://mas.to/@vavakado"
|
href="https://mas.to/@vavakado"
|
||||||
class="group relative rounded-2xl px-6 py-4 text-sm font-semibold leading-6 text-zinc-900 sm:py-6"
|
class="group relative rounded-2xl px-6 py-4 text-sm font-semibold leading-6 text-zinc-900 sm:py-6"
|
||||||
>
|
>
|
||||||
<span class="absolute inset-0 rounded-2xl bg-zinc-50 transition group-hover:bg-zinc-100 sm:group-hover:scale-105">
|
<span class="absolute inset-0 rounded-2xl bg-zinc-50 transition group-hover:bg-zinc-100 sm:group-hover:scale-105 dark:bg-zinc-800 dark:group-hover:bg-zinc-900/95">
|
||||||
</span>
|
</span>
|
||||||
<span class="relative flex items-center gap-4 sm:flex-col">
|
<span class="relative flex items-center gap-4 sm:flex-col dark:text-white">
|
||||||
<svg viewBox="0 0 24 24" aria-hidden="true" class="h-6 w-6">
|
<svg viewBox="0 0 24 24" aria-hidden="true" class="h-6 w-6">
|
||||||
<path d="M11.19 12.195c2.016-.24 3.77-1.475 3.99-2.603.348-1.778.32-4.339.32-4.339 0-3.47-2.286-4.488-2.286-4.488C12.062.238 10.083.017 8.027 0h-.05C5.92.017 3.942.238 2.79.765c0 0-2.285 1.017-2.285 4.488l-.002.662c-.004.64-.007 1.35.011 2.091.083 3.394.626 6.74 3.78 7.57 1.454.383 2.703.463 3.709.408 1.823-.1 2.847-.647 2.847-.647l-.06-1.317s-1.303.41-2.767.36c-1.45-.05-2.98-.156-3.215-1.928a4 4 0 0 1-.033-.496s1.424.346 3.228.428c1.103.05 2.137-.064 3.188-.189zm1.613-2.47H11.13v-4.08c0-.859-.364-1.295-1.091-1.295-.804 0-1.207.517-1.207 1.541v2.233H7.168V5.89c0-1.024-.403-1.541-1.207-1.541-.727 0-1.091.436-1.091 1.296v4.079H3.197V5.522q0-1.288.66-2.046c.456-.505 1.052-.764 1.793-.764.856 0 1.504.328 1.933.983L8 4.39l.417-.695c.429-.655 1.077-.983 1.934-.983.74 0 1.336.259 1.791.764q.662.757.661 2.046z" />
|
<path d="M11.19 12.195c2.016-.24 3.77-1.475 3.99-2.603.348-1.778.32-4.339.32-4.339 0-3.47-2.286-4.488-2.286-4.488C12.062.238 10.083.017 8.027 0h-.05C5.92.017 3.942.238 2.79.765c0 0-2.285 1.017-2.285 4.488l-.002.662c-.004.64-.007 1.35.011 2.091.083 3.394.626 6.74 3.78 7.57 1.454.383 2.703.463 3.709.408 1.823-.1 2.847-.647 2.847-.647l-.06-1.317s-1.303.41-2.767.36c-1.45-.05-2.98-.156-3.215-1.928a4 4 0 0 1-.033-.496s1.424.346 3.228.428c1.103.05 2.137-.064 3.188-.189zm1.613-2.47H11.13v-4.08c0-.859-.364-1.295-1.091-1.295-.804 0-1.207.517-1.207 1.541v2.233H7.168V5.89c0-1.024-.403-1.541-1.207-1.541-.727 0-1.091.436-1.091 1.296v4.079H3.197V5.522q0-1.288.66-2.046c.456-.505 1.052-.764 1.793-.764.856 0 1.504.328 1.933.983L8 4.39l.417-.695c.429-.655 1.077-.983 1.934-.983.74 0 1.336.259 1.791.764q.662.757.661 2.046z" />
|
||||||
</svg>
|
</svg>
|
||||||
|
@ -93,7 +93,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="text-center py-8 font-bold text-3xl text-red-600">
|
<div class="text-center py-8 font-bold text-3xl text-red-600">
|
||||||
<.link navigate={~p"/exams"} class="underline hover:text-red-400 transition-all ease-in-out">
|
<.link navigate={~p"/exams"} class="underline hover:text-red-400 transition-all ease-in-out">
|
||||||
exams
|
{gettext("Exams")}
|
||||||
</.link>
|
</.link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
7
lib/exmr_web/helpers.ex
Normal file
7
lib/exmr_web/helpers.ex
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
defmodule ExmrWeb.LiveHelpers do
|
||||||
|
def on_mount(:default, _params, session, socket) do
|
||||||
|
locale = session["locale"] || "en"
|
||||||
|
Gettext.put_locale(locale)
|
||||||
|
{:cont, socket}
|
||||||
|
end
|
||||||
|
end
|
|
@ -8,8 +8,10 @@ defmodule ExmrWeb.ExamLive.FormComponent do
|
||||||
~H"""
|
~H"""
|
||||||
<div>
|
<div>
|
||||||
<.header>
|
<.header>
|
||||||
<%= @title %>
|
{@title}
|
||||||
<:subtitle>Use this form to manage exam records in your database.</:subtitle>
|
<:subtitle>
|
||||||
|
{gettext("Use this form to manage exam records in your database.")}
|
||||||
|
</:subtitle>
|
||||||
</.header>
|
</.header>
|
||||||
|
|
||||||
<.simple_form
|
<.simple_form
|
||||||
|
@ -19,11 +21,11 @@ defmodule ExmrWeb.ExamLive.FormComponent do
|
||||||
phx-change="validate"
|
phx-change="validate"
|
||||||
phx-submit="save"
|
phx-submit="save"
|
||||||
>
|
>
|
||||||
<.input field={@form[:subject]} type="text" label="Subject" />
|
<.input field={@form[:subject]} type="text" label={gettext("Subject")} />
|
||||||
<.input field={@form[:description]} type="text" label="Description" />
|
<.input field={@form[:description]} type="text" label={gettext("Description")} />
|
||||||
<.input field={@form[:date]} type="date" label="Date" />
|
<.input field={@form[:date]} type="date" label={gettext("Date")} />
|
||||||
<:actions>
|
<:actions>
|
||||||
<.button phx-disable-with="Saving...">Save Exam</.button>
|
<.button phx-disable-with={gettext("Saving...")}>{gettext("Save Exam")}</.button>
|
||||||
</:actions>
|
</:actions>
|
||||||
</.simple_form>
|
</.simple_form>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -12,6 +12,8 @@ defmodule ExmrWeb.ExamLive.Index do
|
||||||
|> assign(:sort_by, "date")
|
|> assign(:sort_by, "date")
|
||||||
|> assign(:live_action, :index)
|
|> assign(:live_action, :index)
|
||||||
|> assign(exam: %{})
|
|> assign(exam: %{})
|
||||||
|
|> assign(events: Enum.group_by(Exams.list_exams(), & &1.date))
|
||||||
|
|> assign(current_month: Date.utc_today())
|
||||||
|
|
||||||
{:ok, socket}
|
{:ok, socket}
|
||||||
end
|
end
|
||||||
|
@ -29,7 +31,7 @@ defmodule ExmrWeb.ExamLive.Index do
|
||||||
|
|
||||||
defp apply_action(socket, :new, _params) do
|
defp apply_action(socket, :new, _params) do
|
||||||
socket
|
socket
|
||||||
|> assign(:page_title, "New Exam")
|
|> assign(:page_title, gettext("New Exam"))
|
||||||
|> assign(:exam, %Exam{})
|
|> assign(:exam, %Exam{})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -44,6 +46,7 @@ defmodule ExmrWeb.ExamLive.Index do
|
||||||
socket =
|
socket =
|
||||||
socket
|
socket
|
||||||
|> assign(:exams, [exam | socket.assigns.exams])
|
|> assign(:exams, [exam | socket.assigns.exams])
|
||||||
|
|> assign(events: Enum.group_by(Exams.list_exams(), & &1.date))
|
||||||
|
|
||||||
{:noreply, socket}
|
{:noreply, socket}
|
||||||
end
|
end
|
||||||
|
@ -58,6 +61,7 @@ defmodule ExmrWeb.ExamLive.Index do
|
||||||
|> update(:exams, fn exams ->
|
|> update(:exams, fn exams ->
|
||||||
Enum.reject(exams, fn l -> l.id == exam.id end)
|
Enum.reject(exams, fn l -> l.id == exam.id end)
|
||||||
end)
|
end)
|
||||||
|
|> assign(events: Enum.group_by(Exams.list_exams(), & &1.date))
|
||||||
|
|
||||||
{:noreply, socket}
|
{:noreply, socket}
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,14 +1,27 @@
|
||||||
<.header>
|
<.header>
|
||||||
Listing Exams
|
{gettext("Listing Exams")}
|
||||||
<div>
|
<div>
|
||||||
<button phx-click="sort" phx-value-by="subject" class="font-light">Sort by Subject</button>
|
<button
|
||||||
<a>|</a>
|
phx-click="sort"
|
||||||
|
phx-value-by="subject"
|
||||||
<button phx-click="sort" phx-value-by="date" class="font-light">Sort by Date</button>
|
class="font-light text-xs md:text-sm lg:text-base"
|
||||||
|
>
|
||||||
|
{gettext("Sort by Subject")}
|
||||||
|
</button>
|
||||||
|
<a class="invisible lg:visible">|</a>
|
||||||
|
<button
|
||||||
|
phx-click="sort"
|
||||||
|
phx-value-by="date"
|
||||||
|
class="font-light text-xs md:text-sm lg:text-base"
|
||||||
|
>
|
||||||
|
{gettext("Sort by Date")}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<:actions>
|
<:actions>
|
||||||
<.link patch={~p"/exams/new"}>
|
<.link patch={~p"/exams/new"}>
|
||||||
<.button>New Exam</.button>
|
<.button class="dark:bg-sky-300 text-white dark:text-black/80 dark:hover:bg-sky-400">
|
||||||
|
{gettext("New Exam")}
|
||||||
|
</.button>
|
||||||
</.link>
|
</.link>
|
||||||
</:actions>
|
</:actions>
|
||||||
</.header>
|
</.header>
|
||||||
|
@ -16,22 +29,32 @@
|
||||||
<div class="divide-y">
|
<div class="divide-y">
|
||||||
<div :for={exam <- @exams} class="py-2 flex gap-2">
|
<div :for={exam <- @exams} class="py-2 flex gap-2">
|
||||||
<div class="grow">
|
<div class="grow">
|
||||||
<div class="font-bold"><%= exam.subject %></div>
|
<div class="font-bold text-xs md:text-sm lg:text-base">{exam.subject}</div>
|
||||||
<div class="font-sm"><%= exam.date %></div>
|
<div class="text-xs md:text-sm lg:text-base">
|
||||||
|
{exam.date} |
|
||||||
|
<b class="text-xs md:text-sm lg:text-base">
|
||||||
|
{case Date.diff(exam.date, Date.utc_today()) do
|
||||||
|
0 -> gettext("Today")
|
||||||
|
1 -> gettext("Tommorow")
|
||||||
|
x when x > 1 -> ngettext("Tomorrow", "%{count} days left", x)
|
||||||
|
x when x < 0 -> ngettext("Yesterday", "%{count} days passed", x * -1)
|
||||||
|
end}
|
||||||
|
</b>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
phx-click="edit"
|
phx-click="edit"
|
||||||
phx-value-id={exam.id}
|
phx-value-id={exam.id}
|
||||||
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-3 rounded-md"
|
class="bg-blue-500 hover:bg-blue-700 text-white font-bold p-1 text-xs md:text-sm lg:py-2 lg:px-3 rounded-md"
|
||||||
>
|
>
|
||||||
Edit
|
{gettext("Edit")}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
phx-click="remove"
|
phx-click="remove"
|
||||||
phx-value-id={exam.id}
|
phx-value-id={exam.id}
|
||||||
class="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-3 rounded-md"
|
class="bg-red-500 hover:bg-red-700 text-white font-bold p-1 text-xs md:text-sm lg:py-2 lg:px-3 rounded-md"
|
||||||
>
|
>
|
||||||
Remove
|
{gettext("Remove")}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -46,3 +69,53 @@
|
||||||
patch={~p"/exams"}
|
patch={~p"/exams"}
|
||||||
/>
|
/>
|
||||||
</.modal>
|
</.modal>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<!-- Navigation -->
|
||||||
|
<!-- <div class="flex justify-between mb-4"> -->
|
||||||
|
<!-- <button phx-click="previous_month" class="px-2 py-1 bg-blue-200">Previous</button> -->
|
||||||
|
<!-- <h2>{@current_month |> Date.to_string() |> String.replace("-", " ")}</h2> -->
|
||||||
|
<!-- <button phx-click="next_month" class="px-2 py-1 bg-blue-200">Next</button> -->
|
||||||
|
<!-- </div> -->
|
||||||
|
|
||||||
|
<!-- Calendar -->
|
||||||
|
<div class="grid grid-cols-7 gap-1" id="calendar-container" phx-update="replace">
|
||||||
|
<!-- Weekdays -->
|
||||||
|
<%= for weekday <- ~w(#{gettext("Sun")} #{gettext("Mon")} #{gettext("Tue")} #{gettext("Wed")} #{gettext("Thu")}
|
||||||
|
#{gettext("Fri")} #{gettext("Sat")}) do %>
|
||||||
|
<div class="font-bold text-center">{weekday}</div>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<!-- Empty spaces for previous month -->
|
||||||
|
<%= for _ <- 1..(Date.day_of_week(Date.new!(@current_month.year, @current_month.month, 1))) do %>
|
||||||
|
<div></div>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<!-- Days of the month -->
|
||||||
|
<%= for day <- 1..Date.days_in_month(@current_month) do %>
|
||||||
|
<% {:ok, date} = Date.new(@current_month.year, @current_month.month, day) %>
|
||||||
|
|
||||||
|
<div
|
||||||
|
id={"day-#{Date.to_string(date)}"}
|
||||||
|
class={[
|
||||||
|
"border rounded p-0.5 md:p-2 lg:p-3 w-full h-12 md:h-20 hover:scale-125 transition-transform ease-in-out duration-100",
|
||||||
|
@current_month == date &&
|
||||||
|
"bg-zinc-200 dark:bg-zinc-800 border-green-500 dark:border-green-300",
|
||||||
|
@current_month != date &&
|
||||||
|
"bg-white dark:bg-zinc-950 dark:border-zinc-400 "
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<div class="text-xs md:text-sm lg:text-base font-bold">{day}</div>
|
||||||
|
<!-- Render events for the current day -->
|
||||||
|
|
||||||
|
<%= if Map.has_key?(@events, date) do %>
|
||||||
|
<ul class="text-xs">
|
||||||
|
<%= for event <- @events[date] do %>
|
||||||
|
<li id={"event-#{event.id}"}>{event.subject}</li>
|
||||||
|
<% end %>
|
||||||
|
</ul>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<.header>
|
<.header>
|
||||||
exam №<%= @exam.id %>
|
exam №{@exam.id}
|
||||||
<:subtitle>очередная контрольная работа по <strong><%= @exam.subject %></strong></:subtitle>
|
<:subtitle>очередная контрольная работа по <strong>{@exam.subject}</strong></:subtitle>
|
||||||
<:actions>
|
<:actions>
|
||||||
<.link patch={~p"/exams/#{@exam}/show/edit"} phx-click={JS.push_focus()}>
|
<.link patch={~p"/exams/#{@exam}/show/edit"} phx-click={JS.push_focus()}>
|
||||||
<.button>изменить</.button>
|
<.button>изменить</.button>
|
||||||
|
@ -9,8 +9,8 @@
|
||||||
</.header>
|
</.header>
|
||||||
|
|
||||||
<.list>
|
<.list>
|
||||||
<:item title="Description"><%= @exam.description %></:item>
|
<:item title="Description">{@exam.description}</:item>
|
||||||
<:item title="Date"><%= @exam.date %></:item>
|
<:item title="Date">{@exam.date}</:item>
|
||||||
</.list>
|
</.list>
|
||||||
|
|
||||||
<.back navigate={~p"/exams"}>Back to exams</.back>
|
<.back navigate={~p"/exams"}>Back to exams</.back>
|
||||||
|
|
|
@ -14,7 +14,7 @@ defmodule ExmrWeb.UserForgotPasswordLive do
|
||||||
<.simple_form for={@form} id="reset_password_form" phx-submit="send_email">
|
<.simple_form for={@form} id="reset_password_form" phx-submit="send_email">
|
||||||
<.input field={@form[:email]} type="email" placeholder="Email" required />
|
<.input field={@form[:email]} type="email" placeholder="Email" required />
|
||||||
<:actions>
|
<:actions>
|
||||||
<.button phx-disable-with="Sending..." class="w-full">
|
<.button phx-disable-with="Sending..." class="w-full dark:text-black dark:bg-red-600">
|
||||||
Send password reset instructions
|
Send password reset instructions
|
||||||
</.button>
|
</.button>
|
||||||
</:actions>
|
</:actions>
|
||||||
|
|
|
@ -26,7 +26,7 @@ defmodule ExmrWeb.UserLoginLive do
|
||||||
</.link>
|
</.link>
|
||||||
</:actions>
|
</:actions>
|
||||||
<:actions>
|
<:actions>
|
||||||
<.button phx-disable-with="Logging in..." class="w-full">
|
<.button phx-disable-with="Logging in..." class="w-full dark:bg-teal-600">
|
||||||
Log in <span aria-hidden="true">→</span>
|
Log in <span aria-hidden="true">→</span>
|
||||||
</.button>
|
</.button>
|
||||||
</:actions>
|
</:actions>
|
||||||
|
|
|
@ -6,7 +6,7 @@ defmodule ExmrWeb.UserRegistrationLive do
|
||||||
|
|
||||||
def render(assigns) do
|
def render(assigns) do
|
||||||
~H"""
|
~H"""
|
||||||
<div class="mx-auto max-w-sm">
|
<div class="mx-auto max-w-sm dark:bg-zinc-950">
|
||||||
<.header class="text-center">
|
<.header class="text-center">
|
||||||
Register for an account
|
Register for an account
|
||||||
<:subtitle>
|
<:subtitle>
|
||||||
|
@ -35,7 +35,9 @@ defmodule ExmrWeb.UserRegistrationLive do
|
||||||
<.input field={@form[:password]} type="password" label="Password" required />
|
<.input field={@form[:password]} type="password" label="Password" required />
|
||||||
|
|
||||||
<:actions>
|
<:actions>
|
||||||
<.button phx-disable-with="Creating account..." class="w-full">Create an account</.button>
|
<.button phx-disable-with="Creating account..." class="w-full dark:bg-green-800">
|
||||||
|
Create an account
|
||||||
|
</.button>
|
||||||
</:actions>
|
</:actions>
|
||||||
</.simple_form>
|
</.simple_form>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -6,8 +6,8 @@ defmodule ExmrWeb.UserSettingsLive do
|
||||||
def render(assigns) do
|
def render(assigns) do
|
||||||
~H"""
|
~H"""
|
||||||
<.header class="text-center">
|
<.header class="text-center">
|
||||||
Account Settings
|
{gettext("Account Settings")}
|
||||||
<:subtitle>Manage your account email address and password settings</:subtitle>
|
<:subtitle>{gettext("Manage your account email address and password settings")}</:subtitle>
|
||||||
</.header>
|
</.header>
|
||||||
|
|
||||||
<div class="space-y-12 divide-y">
|
<div class="space-y-12 divide-y">
|
||||||
|
@ -18,18 +18,20 @@ defmodule ExmrWeb.UserSettingsLive do
|
||||||
phx-submit="update_email"
|
phx-submit="update_email"
|
||||||
phx-change="validate_email"
|
phx-change="validate_email"
|
||||||
>
|
>
|
||||||
<.input field={@email_form[:email]} type="email" label="Email" required />
|
<.input field={@email_form[:email]} type="email" label={gettext("Email")} required />
|
||||||
<.input
|
<.input
|
||||||
field={@email_form[:current_password]}
|
field={@email_form[:current_password]}
|
||||||
name="current_password"
|
name="current_password"
|
||||||
id="current_password_for_email"
|
id="current_password_for_email"
|
||||||
type="password"
|
type="password"
|
||||||
label="Current password"
|
label={gettext("Current password")}
|
||||||
value={@email_form_current_password}
|
value={@email_form_current_password}
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<:actions>
|
<:actions>
|
||||||
<.button phx-disable-with="Changing...">Change Email</.button>
|
<.button phx-disable-with={gettext("Changing...")}>
|
||||||
|
{gettext("Change Email")}
|
||||||
|
</.button>
|
||||||
</:actions>
|
</:actions>
|
||||||
</.simple_form>
|
</.simple_form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -49,23 +51,30 @@ defmodule ExmrWeb.UserSettingsLive do
|
||||||
id="hidden_user_email"
|
id="hidden_user_email"
|
||||||
value={@current_email}
|
value={@current_email}
|
||||||
/>
|
/>
|
||||||
<.input field={@password_form[:password]} type="password" label="New password" required />
|
<.input
|
||||||
|
field={@password_form[:password]}
|
||||||
|
type="password"
|
||||||
|
label={gettext("New password")}
|
||||||
|
required
|
||||||
|
/>
|
||||||
<.input
|
<.input
|
||||||
field={@password_form[:password_confirmation]}
|
field={@password_form[:password_confirmation]}
|
||||||
type="password"
|
type="password"
|
||||||
label="Confirm new password"
|
label={gettext("Confirm new password")}
|
||||||
/>
|
/>
|
||||||
<.input
|
<.input
|
||||||
field={@password_form[:current_password]}
|
field={@password_form[:current_password]}
|
||||||
name="current_password"
|
name="current_password"
|
||||||
type="password"
|
type="password"
|
||||||
label="Current password"
|
label={gettext("Current password")}
|
||||||
id="current_password_for_password"
|
id="current_password_for_password"
|
||||||
value={@current_password}
|
value={@current_password}
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<:actions>
|
<:actions>
|
||||||
<.button phx-disable-with="Changing...">Change Password</.button>
|
<.button phx-disable-with={gettext("Changing...")}>
|
||||||
|
{gettext("Change Password")}
|
||||||
|
</.button>
|
||||||
</:actions>
|
</:actions>
|
||||||
</.simple_form>
|
</.simple_form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -77,10 +86,10 @@ defmodule ExmrWeb.UserSettingsLive do
|
||||||
socket =
|
socket =
|
||||||
case Users.update_user_email(socket.assigns.current_user, token) do
|
case Users.update_user_email(socket.assigns.current_user, token) do
|
||||||
:ok ->
|
:ok ->
|
||||||
put_flash(socket, :info, "Email changed successfully.")
|
put_flash(socket, :info, gettext("Email changed successfully."))
|
||||||
|
|
||||||
:error ->
|
:error ->
|
||||||
put_flash(socket, :error, "Email change link is invalid or it has expired.")
|
put_flash(socket, :error, gettext("Email change link is invalid or it has expired."))
|
||||||
end
|
end
|
||||||
|
|
||||||
{:ok, push_navigate(socket, to: ~p"/users/settings")}
|
{:ok, push_navigate(socket, to: ~p"/users/settings")}
|
||||||
|
@ -127,7 +136,7 @@ defmodule ExmrWeb.UserSettingsLive do
|
||||||
&url(~p"/users/settings/confirm_email/#{&1}")
|
&url(~p"/users/settings/confirm_email/#{&1}")
|
||||||
)
|
)
|
||||||
|
|
||||||
info = "A link to confirm your email change has been sent to the new address."
|
info = gettext("A link to confirm your email change has been sent to the new address.")
|
||||||
{:noreply, socket |> put_flash(:info, info) |> assign(email_form_current_password: nil)}
|
{:noreply, socket |> put_flash(:info, info) |> assign(email_form_current_password: nil)}
|
||||||
|
|
||||||
{:error, changeset} ->
|
{:error, changeset} ->
|
||||||
|
|
67
lib/exmr_web/plug/local_plug.ex
Normal file
67
lib/exmr_web/plug/local_plug.ex
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
defmodule ExmrWeb.Plugs.Locale do
|
||||||
|
import Plug.Conn
|
||||||
|
|
||||||
|
def init(_opts), do: nil
|
||||||
|
|
||||||
|
def call(conn, _opts) do
|
||||||
|
accepted_languages = extract_accept_language(conn)
|
||||||
|
known_locales = Gettext.known_locales(ExmrWeb.Gettext)
|
||||||
|
|
||||||
|
accepted_languages =
|
||||||
|
known_locales --
|
||||||
|
known_locales -- accepted_languages
|
||||||
|
|
||||||
|
case accepted_languages do
|
||||||
|
[locale | _] ->
|
||||||
|
Gettext.put_locale(ExmrWeb.Gettext, locale)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_session(:locale, locale)
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
conn
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Copied from
|
||||||
|
# https://raw.githubusercontent.com/smeevil/set_locale/fd35624e25d79d61e70742e42ade955e5ff857b8/lib/headers.ex
|
||||||
|
def extract_accept_language(conn) do
|
||||||
|
case Plug.Conn.get_req_header(conn, "accept-language") do
|
||||||
|
[value | _] ->
|
||||||
|
value
|
||||||
|
|> String.split(",")
|
||||||
|
|> Enum.map(&parse_language_option/1)
|
||||||
|
|> Enum.sort(&(&1.quality > &2.quality))
|
||||||
|
|> Enum.map(& &1.tag)
|
||||||
|
|> Enum.reject(&is_nil/1)
|
||||||
|
|> ensure_language_fallbacks()
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp parse_language_option(string) do
|
||||||
|
captures = Regex.named_captures(~r/^\s?(?<tag>[\w\-]+)(?:;q=(?<quality>[\d\.]+))?$/i, string)
|
||||||
|
|
||||||
|
quality =
|
||||||
|
case Float.parse(captures["quality"] || "1.0") do
|
||||||
|
{val, _} -> val
|
||||||
|
_ -> 1.0
|
||||||
|
end
|
||||||
|
|
||||||
|
%{tag: captures["tag"], quality: quality}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp ensure_language_fallbacks(tags) do
|
||||||
|
Enum.flat_map(tags, fn tag ->
|
||||||
|
case String.split(tag, "-") do
|
||||||
|
[language, _country_variant] ->
|
||||||
|
if Enum.member?(tags, language), do: [tag], else: [tag, language]
|
||||||
|
|
||||||
|
[_language] ->
|
||||||
|
[tag]
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end
|
|
@ -12,6 +12,7 @@ defmodule ExmrWeb.Router do
|
||||||
plug :put_root_layout, html: {ExmrWeb.Layouts, :root}
|
plug :put_root_layout, html: {ExmrWeb.Layouts, :root}
|
||||||
plug :protect_from_forgery
|
plug :protect_from_forgery
|
||||||
plug :put_secure_browser_headers
|
plug :put_secure_browser_headers
|
||||||
|
plug ExmrWeb.Plugs.Locale
|
||||||
plug :fetch_current_user
|
plug :fetch_current_user
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -53,7 +54,7 @@ defmodule ExmrWeb.Router do
|
||||||
pipe_through [:browser, :redirect_if_user_is_authenticated]
|
pipe_through [:browser, :redirect_if_user_is_authenticated]
|
||||||
|
|
||||||
live_session :redirect_if_user_is_authenticated,
|
live_session :redirect_if_user_is_authenticated,
|
||||||
on_mount: [{ExmrWeb.UserAuth, :redirect_if_user_is_authenticated}] do
|
on_mount: [{ExmrWeb.UserAuth, :redirect_if_user_is_authenticated}, ExmrWeb.LiveHelpers] do
|
||||||
if Exmr.enable_registration() != "false" do
|
if Exmr.enable_registration() != "false" do
|
||||||
live "/users/register", UserRegistrationLive, :new
|
live "/users/register", UserRegistrationLive, :new
|
||||||
end
|
end
|
||||||
|
@ -70,7 +71,7 @@ defmodule ExmrWeb.Router do
|
||||||
pipe_through [:browser, :require_authenticated_user]
|
pipe_through [:browser, :require_authenticated_user]
|
||||||
|
|
||||||
live_session :require_authenticated_user,
|
live_session :require_authenticated_user,
|
||||||
on_mount: [{ExmrWeb.UserAuth, :ensure_authenticated}] do
|
on_mount: [{ExmrWeb.UserAuth, :ensure_authenticated}, ExmrWeb.LiveHelpers] do
|
||||||
if Exmr.enable_registration() == "false" do
|
if Exmr.enable_registration() == "false" do
|
||||||
live "/users/register", UserRegistrationLive, :new
|
live "/users/register", UserRegistrationLive, :new
|
||||||
end
|
end
|
||||||
|
@ -93,7 +94,7 @@ defmodule ExmrWeb.Router do
|
||||||
delete "/users/log_out", UserSessionController, :delete
|
delete "/users/log_out", UserSessionController, :delete
|
||||||
|
|
||||||
live_session :current_user,
|
live_session :current_user,
|
||||||
on_mount: [{ExmrWeb.UserAuth, :mount_current_user}] do
|
on_mount: [{ExmrWeb.UserAuth, :mount_current_user}, ExmrWeb.LiveHelpers] do
|
||||||
live "/users/confirm/:token", UserConfirmationLive, :edit
|
live "/users/confirm/:token", UserConfirmationLive, :edit
|
||||||
live "/users/confirm", UserConfirmationInstructionsLive, :new
|
live "/users/confirm", UserConfirmationInstructionsLive, :new
|
||||||
end
|
end
|
||||||
|
|
5
mix.exs
5
mix.exs
|
@ -4,7 +4,7 @@ defmodule Exmr.MixProject do
|
||||||
def project do
|
def project do
|
||||||
[
|
[
|
||||||
app: :exmr,
|
app: :exmr,
|
||||||
version: "0.1.0",
|
version: "0.2.2",
|
||||||
elixir: "~> 1.14",
|
elixir: "~> 1.14",
|
||||||
elixirc_paths: elixirc_paths(Mix.env()),
|
elixirc_paths: elixirc_paths(Mix.env()),
|
||||||
start_permanent: Mix.env() == :prod,
|
start_permanent: Mix.env() == :prod,
|
||||||
|
@ -40,8 +40,7 @@ defmodule Exmr.MixProject do
|
||||||
{:credo, "~> 1.7", only: [:dev, :test], runtime: false},
|
{:credo, "~> 1.7", only: [:dev, :test], runtime: false},
|
||||||
{:phoenix_html, "~> 4.1"},
|
{:phoenix_html, "~> 4.1"},
|
||||||
{:phoenix_live_reload, "~> 1.2", only: :dev},
|
{:phoenix_live_reload, "~> 1.2", only: :dev},
|
||||||
# TODO bump on release to {:phoenix_live_view, "~> 1.0.0"},
|
{:phoenix_live_view, "~> 1.0.0"},
|
||||||
{:phoenix_live_view, "~> 1.0.0-rc.1", override: true},
|
|
||||||
{:floki, ">= 0.30.0", only: :test},
|
{:floki, ">= 0.30.0", only: :test},
|
||||||
{:phoenix_live_dashboard, "~> 0.8.3"},
|
{:phoenix_live_dashboard, "~> 0.8.3"},
|
||||||
{:esbuild, "~> 0.8", runtime: Mix.env() == :dev},
|
{:esbuild, "~> 0.8", runtime: Mix.env() == :dev},
|
||||||
|
|
12
mix.lock
12
mix.lock
|
@ -1,5 +1,5 @@
|
||||||
%{
|
%{
|
||||||
"bandit": {:hex, :bandit, "1.5.7", "6856b1e1df4f2b0cb3df1377eab7891bec2da6a7fd69dc78594ad3e152363a50", [:mix], [{:hpax, "~> 1.0.0", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:thousand_island, "~> 1.0", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "f2dd92ae87d2cbea2fa9aa1652db157b6cba6c405cb44d4f6dd87abba41371cd"},
|
"bandit": {:hex, :bandit, "1.6.1", "9e01b93d72ddc21d8c576a704949e86ee6cde7d11270a1d3073787876527a48f", [:mix], [{:hpax, "~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:thousand_island, "~> 1.0", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "5a904bf010ea24b67979835e0507688e31ac873d4ffc8ed0e5413e8d77455031"},
|
||||||
"bcrypt_elixir": {:hex, :bcrypt_elixir, "3.2.0", "feab711974beba4cb348147170346fe097eea2e840db4e012a145e180ed4ab75", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "563e92a6c77d667b19c5f4ba17ab6d440a085696bdf4c68b9b0f5b30bc5422b8"},
|
"bcrypt_elixir": {:hex, :bcrypt_elixir, "3.2.0", "feab711974beba4cb348147170346fe097eea2e840db4e012a145e180ed4ab75", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "563e92a6c77d667b19c5f4ba17ab6d440a085696bdf4c68b9b0f5b30bc5422b8"},
|
||||||
"bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"},
|
"bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"},
|
||||||
"castore": {:hex, :castore, "1.0.10", "43bbeeac820f16c89f79721af1b3e092399b3a1ecc8df1a472738fd853574911", [:mix], [], "hexpm", "1b0b7ea14d889d9ea21202c43a4fa015eb913021cb535e8ed91946f4b77a8848"},
|
"castore": {:hex, :castore, "1.0.10", "43bbeeac820f16c89f79721af1b3e092399b3a1ecc8df1a472738fd853574911", [:mix], [], "hexpm", "1b0b7ea14d889d9ea21202c43a4fa015eb913021cb535e8ed91946f4b77a8848"},
|
||||||
|
@ -15,21 +15,21 @@
|
||||||
"expo": {:hex, :expo, "1.1.0", "f7b9ed7fb5745ebe1eeedf3d6f29226c5dd52897ac67c0f8af62a07e661e5c75", [:mix], [], "hexpm", "fbadf93f4700fb44c331362177bdca9eeb8097e8b0ef525c9cc501cb9917c960"},
|
"expo": {:hex, :expo, "1.1.0", "f7b9ed7fb5745ebe1eeedf3d6f29226c5dd52897ac67c0f8af62a07e661e5c75", [:mix], [], "hexpm", "fbadf93f4700fb44c331362177bdca9eeb8097e8b0ef525c9cc501cb9917c960"},
|
||||||
"file_system": {:hex, :file_system, "1.0.1", "79e8ceaddb0416f8b8cd02a0127bdbababe7bf4a23d2a395b983c1f8b3f73edd", [:mix], [], "hexpm", "4414d1f38863ddf9120720cd976fce5bdde8e91d8283353f0e31850fa89feb9e"},
|
"file_system": {:hex, :file_system, "1.0.1", "79e8ceaddb0416f8b8cd02a0127bdbababe7bf4a23d2a395b983c1f8b3f73edd", [:mix], [], "hexpm", "4414d1f38863ddf9120720cd976fce5bdde8e91d8283353f0e31850fa89feb9e"},
|
||||||
"finch": {:hex, :finch, "0.19.0", "c644641491ea854fc5c1bbaef36bfc764e3f08e7185e1f084e35e0672241b76d", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.6.2 or ~> 1.7", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.1", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "fc5324ce209125d1e2fa0fcd2634601c52a787aff1cd33ee833664a5af4ea2b6"},
|
"finch": {:hex, :finch, "0.19.0", "c644641491ea854fc5c1bbaef36bfc764e3f08e7185e1f084e35e0672241b76d", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.6.2 or ~> 1.7", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.1", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "fc5324ce209125d1e2fa0fcd2634601c52a787aff1cd33ee833664a5af4ea2b6"},
|
||||||
"floki": {:hex, :floki, "0.36.3", "1102f93b16a55bc5383b85ae3ec470f82dee056eaeff9195e8afdf0ef2a43c30", [:mix], [], "hexpm", "fe0158bff509e407735f6d40b3ee0d7deb47f3f3ee7c6c182ad28599f9f6b27a"},
|
"floki": {:hex, :floki, "0.37.0", "b83e0280bbc6372f2a403b2848013650b16640cd2470aea6701f0632223d719e", [:mix], [], "hexpm", "516a0c15a69f78c47dc8e0b9b3724b29608aa6619379f91b1ffa47109b5d0dd3"},
|
||||||
"gettext": {:hex, :gettext, "0.26.2", "5978aa7b21fada6deabf1f6341ddba50bc69c999e812211903b169799208f2a8", [:mix], [{:expo, "~> 0.5.1 or ~> 1.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "aa978504bcf76511efdc22d580ba08e2279caab1066b76bb9aa81c4a1e0a32a5"},
|
"gettext": {:hex, :gettext, "0.26.2", "5978aa7b21fada6deabf1f6341ddba50bc69c999e812211903b169799208f2a8", [:mix], [{:expo, "~> 0.5.1 or ~> 1.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "aa978504bcf76511efdc22d580ba08e2279caab1066b76bb9aa81c4a1e0a32a5"},
|
||||||
"heroicons": {:git, "https://github.com/tailwindlabs/heroicons.git", "88ab3a0d790e6a47404cba02800a6b25d2afae50", [tag: "v2.1.1", sparse: "optimized", depth: 1]},
|
"heroicons": {:git, "https://github.com/tailwindlabs/heroicons.git", "88ab3a0d790e6a47404cba02800a6b25d2afae50", [tag: "v2.1.1", sparse: "optimized", depth: 1]},
|
||||||
"hpax": {:hex, :hpax, "1.0.0", "28dcf54509fe2152a3d040e4e3df5b265dcb6cb532029ecbacf4ce52caea3fd2", [:mix], [], "hexpm", "7f1314731d711e2ca5fdc7fd361296593fc2542570b3105595bb0bc6d0fad601"},
|
"hpax": {:hex, :hpax, "1.0.1", "c857057f89e8bd71d97d9042e009df2a42705d6d690d54eca84c8b29af0787b0", [:mix], [], "hexpm", "4e2d5a4f76ae1e3048f35ae7adb1641c36265510a2d4638157fbcb53dda38445"},
|
||||||
"jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"},
|
"jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"},
|
||||||
"mime": {:hex, :mime, "2.0.6", "8f18486773d9b15f95f4f4f1e39b710045fa1de891fada4516559967276e4dc2", [:mix], [], "hexpm", "c9945363a6b26d747389aac3643f8e0e09d30499a138ad64fe8fd1d13d9b153e"},
|
"mime": {:hex, :mime, "2.0.6", "8f18486773d9b15f95f4f4f1e39b710045fa1de891fada4516559967276e4dc2", [:mix], [], "hexpm", "c9945363a6b26d747389aac3643f8e0e09d30499a138ad64fe8fd1d13d9b153e"},
|
||||||
"mint": {:hex, :mint, "1.6.2", "af6d97a4051eee4f05b5500671d47c3a67dac7386045d87a904126fd4bbcea2e", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "5ee441dffc1892f1ae59127f74afe8fd82fda6587794278d924e4d90ea3d63f9"},
|
"mint": {:hex, :mint, "1.6.2", "af6d97a4051eee4f05b5500671d47c3a67dac7386045d87a904126fd4bbcea2e", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "5ee441dffc1892f1ae59127f74afe8fd82fda6587794278d924e4d90ea3d63f9"},
|
||||||
"nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"},
|
"nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"},
|
||||||
"nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"},
|
"nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"},
|
||||||
"phoenix": {:hex, :phoenix, "1.7.14", "a7d0b3f1bc95987044ddada111e77bd7f75646a08518942c72a8440278ae7825", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.7", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "c7859bc56cc5dfef19ecfc240775dae358cbaa530231118a9e014df392ace61a"},
|
"phoenix": {:hex, :phoenix, "1.7.18", "5310c21443514be44ed93c422e15870aef254cf1b3619e4f91538e7529d2b2e4", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.7", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "1797fcc82108442a66f2c77a643a62980f342bfeb63d6c9a515ab8294870004e"},
|
||||||
"phoenix_ecto": {:hex, :phoenix_ecto, "4.6.3", "f686701b0499a07f2e3b122d84d52ff8a31f5def386e03706c916f6feddf69ef", [:mix], [{:ecto, "~> 3.5", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.1", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "909502956916a657a197f94cc1206d9a65247538de8a5e186f7537c895d95764"},
|
"phoenix_ecto": {:hex, :phoenix_ecto, "4.6.3", "f686701b0499a07f2e3b122d84d52ff8a31f5def386e03706c916f6feddf69ef", [:mix], [{:ecto, "~> 3.5", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.1", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "909502956916a657a197f94cc1206d9a65247538de8a5e186f7537c895d95764"},
|
||||||
"phoenix_html": {:hex, :phoenix_html, "4.1.1", "4c064fd3873d12ebb1388425a8f2a19348cef56e7289e1998e2d2fa758aa982e", [:mix], [], "hexpm", "f2f2df5a72bc9a2f510b21497fd7d2b86d932ec0598f0210fed4114adc546c6f"},
|
"phoenix_html": {:hex, :phoenix_html, "4.1.1", "4c064fd3873d12ebb1388425a8f2a19348cef56e7289e1998e2d2fa758aa982e", [:mix], [], "hexpm", "f2f2df5a72bc9a2f510b21497fd7d2b86d932ec0598f0210fed4114adc546c6f"},
|
||||||
"phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.8.5", "d5f44d7dbd7cfacaa617b70c5a14b2b598d6f93b9caa8e350c51d56cd4350a9b", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.5", [hex: :ecto_mysql_extras, repo: "hexpm", optional: true]}, {:ecto_psql_extras, "~> 0.7", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:ecto_sqlite3_extras, "~> 1.1.7 or ~> 1.2.0", [hex: :ecto_sqlite3_extras, repo: "hexpm", optional: true]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.19 or ~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6 or ~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "1d73920515554d7d6c548aee0bf10a4780568b029d042eccb336db29ea0dad70"},
|
"phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.8.5", "d5f44d7dbd7cfacaa617b70c5a14b2b598d6f93b9caa8e350c51d56cd4350a9b", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.5", [hex: :ecto_mysql_extras, repo: "hexpm", optional: true]}, {:ecto_psql_extras, "~> 0.7", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:ecto_sqlite3_extras, "~> 1.1.7 or ~> 1.2.0", [hex: :ecto_sqlite3_extras, repo: "hexpm", optional: true]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.19 or ~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6 or ~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "1d73920515554d7d6c548aee0bf10a4780568b029d042eccb336db29ea0dad70"},
|
||||||
"phoenix_live_reload": {:hex, :phoenix_live_reload, "1.5.3", "f2161c207fda0e4fb55165f650f7f8db23f02b29e3bff00ff7ef161d6ac1f09d", [:mix], [{:file_system, "~> 0.3 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "b4ec9cd73cb01ff1bd1cac92e045d13e7030330b74164297d1aee3907b54803c"},
|
"phoenix_live_reload": {:hex, :phoenix_live_reload, "1.5.3", "f2161c207fda0e4fb55165f650f7f8db23f02b29e3bff00ff7ef161d6ac1f09d", [:mix], [{:file_system, "~> 0.3 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "b4ec9cd73cb01ff1bd1cac92e045d13e7030330b74164297d1aee3907b54803c"},
|
||||||
"phoenix_live_view": {:hex, :phoenix_live_view, "1.0.0-rc.7", "d2abca526422adea88896769529addb6443390b1d4f1ff9cbe694312d8875fb2", [:mix], [{:floki, "~> 0.36", [hex: :floki, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.15", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b82a4575f6f3eb5b97922ec6874b0c52b3ca0cc5dcb4b14ddc478cbfa135dd01"},
|
"phoenix_live_view": {:hex, :phoenix_live_view, "1.0.0", "3a10dfce8f87b2ad4dc65de0732fc2a11e670b2779a19e8d3281f4619a85bce4", [:mix], [{:floki, "~> 0.36", [hex: :floki, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.15", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "254caef0028765965ca6bd104cc7d68dcc7d57cc42912bef92f6b03047251d99"},
|
||||||
"phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.3", "3168d78ba41835aecad272d5e8cd51aa87a7ac9eb836eabc42f6e57538e3731d", [:mix], [], "hexpm", "bba06bc1dcfd8cb086759f0edc94a8ba2bc8896d5331a1e2c2902bf8e36ee502"},
|
"phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.3", "3168d78ba41835aecad272d5e8cd51aa87a7ac9eb836eabc42f6e57538e3731d", [:mix], [], "hexpm", "bba06bc1dcfd8cb086759f0edc94a8ba2bc8896d5331a1e2c2902bf8e36ee502"},
|
||||||
"phoenix_template": {:hex, :phoenix_template, "1.0.4", "e2092c132f3b5e5b2d49c96695342eb36d0ed514c5b252a77048d5969330d639", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "2c0c81f0e5c6753faf5cca2f229c9709919aba34fab866d3bc05060c9c444206"},
|
"phoenix_template": {:hex, :phoenix_template, "1.0.4", "e2092c132f3b5e5b2d49c96695342eb36d0ed514c5b252a77048d5969330d639", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "2c0c81f0e5c6753faf5cca2f229c9709919aba34fab866d3bc05060c9c444206"},
|
||||||
"plug": {:hex, :plug, "1.16.1", "40c74619c12f82736d2214557dedec2e9762029b2438d6d175c5074c933edc9d", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a13ff6b9006b03d7e33874945b2755253841b238c34071ed85b0e86057f8cddc"},
|
"plug": {:hex, :plug, "1.16.1", "40c74619c12f82736d2214557dedec2e9762029b2438d6d175c5074c933edc9d", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a13ff6b9006b03d7e33874945b2755253841b238c34071ed85b0e86057f8cddc"},
|
||||||
|
@ -40,7 +40,7 @@
|
||||||
"telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"},
|
"telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"},
|
||||||
"telemetry_metrics": {:hex, :telemetry_metrics, "1.0.0", "29f5f84991ca98b8eb02fc208b2e6de7c95f8bb2294ef244a176675adc7775df", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "f23713b3847286a534e005126d4c959ebcca68ae9582118ce436b521d1d47d5d"},
|
"telemetry_metrics": {:hex, :telemetry_metrics, "1.0.0", "29f5f84991ca98b8eb02fc208b2e6de7c95f8bb2294ef244a176675adc7775df", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "f23713b3847286a534e005126d4c959ebcca68ae9582118ce436b521d1d47d5d"},
|
||||||
"telemetry_poller": {:hex, :telemetry_poller, "1.1.0", "58fa7c216257291caaf8d05678c8d01bd45f4bdbc1286838a28c4bb62ef32999", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "9eb9d9cbfd81cbd7cdd24682f8711b6e2b691289a0de6826e58452f28c103c8f"},
|
"telemetry_poller": {:hex, :telemetry_poller, "1.1.0", "58fa7c216257291caaf8d05678c8d01bd45f4bdbc1286838a28c4bb62ef32999", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "9eb9d9cbfd81cbd7cdd24682f8711b6e2b691289a0de6826e58452f28c103c8f"},
|
||||||
"thousand_island": {:hex, :thousand_island, "1.3.5", "6022b6338f1635b3d32406ff98d68b843ba73b3aa95cfc27154223244f3a6ca5", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "2be6954916fdfe4756af3239fb6b6d75d0b8063b5df03ba76fd8a4c87849e180"},
|
"thousand_island": {:hex, :thousand_island, "1.3.7", "1da7598c0f4f5f50562c097a3f8af308ded48cd35139f0e6f17d9443e4d0c9c5", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "0139335079953de41d381a6134d8b618d53d084f558c734f2662d1a72818dd12"},
|
||||||
"websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"},
|
"websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"},
|
||||||
"websock_adapter": {:hex, :websock_adapter, "0.5.8", "3b97dc94e407e2d1fc666b2fb9acf6be81a1798a2602294aac000260a7c4a47d", [:mix], [{:bandit, ">= 0.6.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "315b9a1865552212b5f35140ad194e67ce31af45bcee443d4ecb96b5fd3f3782"},
|
"websock_adapter": {:hex, :websock_adapter, "0.5.8", "3b97dc94e407e2d1fc666b2fb9acf6be81a1798a2602294aac000260a7c4a47d", [:mix], [{:bandit, ">= 0.6.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "315b9a1865552212b5f35140ad194e67ce31af45bcee443d4ecb96b5fd3f3782"},
|
||||||
}
|
}
|
||||||
|
|
270
priv/gettext/default.pot
Normal file
270
priv/gettext/default.pot
Normal file
|
@ -0,0 +1,270 @@
|
||||||
|
## This file is a PO Template file.
|
||||||
|
##
|
||||||
|
## "msgid"s here are often extracted from source code.
|
||||||
|
## Add new messages manually only if they're dynamic
|
||||||
|
## messages that can't be statically extracted.
|
||||||
|
##
|
||||||
|
## Run "mix gettext.extract" to bring this file up to
|
||||||
|
## date. Leave "msgstr"s empty as changing them here has no
|
||||||
|
## effect: edit them in PO (.po) files instead.
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/controllers/page_html/home.html.heex:51
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "A simple, modern, and fast exam management system."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/components/core_components.ex:489
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Actions"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/components/core_components.ex:164
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Attempting to reconnect"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/controllers/page_html/home.html.heex:54
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Built using Phoenix LiveView, Ecto, and TailwindCSS by @vavakado"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/index.html.heex:50
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Edit"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/components/core_components.ex:155
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Error!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/controllers/page_html/home.html.heex:96
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Exams"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/components/core_components.ex:176
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Hang in there while we get back on track"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/index.ex:34
|
||||||
|
#: lib/exmr_web/live/exam_live/index.html.heex:23
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "New Exam"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/index.html.heex:57
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Remove"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/components/core_components.ex:171
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Something went wrong!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/index.html.heex:17
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Sort by Date"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/index.html.heex:9
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Sort by Subject"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/components/core_components.ex:154
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Success!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/index.html.heex:37
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Today"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/index.html.heex:39
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Tomorrow"
|
||||||
|
msgid_plural "%{count} days left"
|
||||||
|
msgstr[0] ""
|
||||||
|
msgstr[1] ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/components/core_components.ex:159
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "We can't find the internet"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/components/core_components.ex:80
|
||||||
|
#: lib/exmr_web/components/core_components.ex:134
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "close"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/user_settings_live.ex:139
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "A link to confirm your email change has been sent to the new address."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/user_settings_live.ex:9
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Account Settings"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/user_settings_live.ex:33
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Change Email"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/user_settings_live.ex:76
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Change Password"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/user_settings_live.ex:32
|
||||||
|
#: lib/exmr_web/live/user_settings_live.ex:75
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Changing..."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/user_settings_live.ex:63
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Confirm new password"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/user_settings_live.ex:27
|
||||||
|
#: lib/exmr_web/live/user_settings_live.ex:69
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Current password"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/user_settings_live.ex:21
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Email"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/user_settings_live.ex:92
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Email change link is invalid or it has expired."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/user_settings_live.ex:89
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Email changed successfully."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/components/layouts/root.html.heex:42
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Log out"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/user_settings_live.ex:10
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Manage your account email address and password settings"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/user_settings_live.ex:57
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "New password"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/components/layouts/root.html.heex:33
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Settings"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/form_component.ex:26
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Date"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/form_component.ex:25
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Description"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/form_component.ex:24
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Subject"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/form_component.ex:13
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Use this form to manage exam records in your database."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/index.html.heex:2
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Listing Exams"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/form_component.ex:28
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Save Exam"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/form_component.ex:28
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Saving..."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/index.html.heex:40
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Yesterday"
|
||||||
|
msgid_plural "%{count} days passed"
|
||||||
|
msgstr[0] ""
|
||||||
|
msgstr[1] ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/index.html.heex:38
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Tommorow"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/components/layouts/root.html.heex:59
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Log in"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/components/layouts/root.html.heex:51
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Register"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/index.html.heex:85
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Fri"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/index.html.heex:84
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Mon"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/index.html.heex:85
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Sat"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/index.html.heex:84
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Sun"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/index.html.heex:84
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Thu"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/index.html.heex:84
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Tue"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/index.html.heex:84
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Wed"
|
||||||
|
msgstr ""
|
270
priv/gettext/en/LC_MESSAGES/default.po
Normal file
270
priv/gettext/en/LC_MESSAGES/default.po
Normal file
|
@ -0,0 +1,270 @@
|
||||||
|
## "msgid"s in this file come from POT (.pot) files.
|
||||||
|
###
|
||||||
|
### Do not add, change, or remove "msgid"s manually here as
|
||||||
|
### they're tied to the ones in the corresponding POT file
|
||||||
|
### (with the same domain).
|
||||||
|
###
|
||||||
|
### Use "mix gettext.extract --merge" or "mix gettext.merge"
|
||||||
|
### to merge POT files into PO files.
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Language: en\n"
|
||||||
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
|
||||||
|
#: lib/exmr_web/controllers/page_html/home.html.heex:51
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "A simple, modern, and fast exam management system."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/components/core_components.ex:489
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Actions"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/components/core_components.ex:164
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Attempting to reconnect"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/controllers/page_html/home.html.heex:54
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Built using Phoenix LiveView, Ecto, and TailwindCSS by @vavakado"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/index.html.heex:50
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Edit"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/components/core_components.ex:155
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Error!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/controllers/page_html/home.html.heex:96
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Exams"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/components/core_components.ex:176
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Hang in there while we get back on track"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/index.ex:34
|
||||||
|
#: lib/exmr_web/live/exam_live/index.html.heex:23
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "New Exam"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/index.html.heex:57
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Remove"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/components/core_components.ex:171
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Something went wrong!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/index.html.heex:17
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Sort by Date"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/index.html.heex:9
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Sort by Subject"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/components/core_components.ex:154
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Success!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/index.html.heex:37
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Today"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/index.html.heex:39
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Tomorrow"
|
||||||
|
msgid_plural "%{count} days left"
|
||||||
|
msgstr[0] ""
|
||||||
|
msgstr[1] ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/components/core_components.ex:159
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "We can't find the internet"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/components/core_components.ex:80
|
||||||
|
#: lib/exmr_web/components/core_components.ex:134
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "close"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/user_settings_live.ex:139
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "A link to confirm your email change has been sent to the new address."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/user_settings_live.ex:9
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Account Settings"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/user_settings_live.ex:33
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Change Email"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/user_settings_live.ex:76
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Change Password"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/user_settings_live.ex:32
|
||||||
|
#: lib/exmr_web/live/user_settings_live.ex:75
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Changing..."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/user_settings_live.ex:63
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Confirm new password"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/user_settings_live.ex:27
|
||||||
|
#: lib/exmr_web/live/user_settings_live.ex:69
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Current password"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/user_settings_live.ex:21
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Email"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/user_settings_live.ex:92
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Email change link is invalid or it has expired."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/user_settings_live.ex:89
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Email changed successfully."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/components/layouts/root.html.heex:42
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Log out"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/user_settings_live.ex:10
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Manage your account email address and password settings"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/user_settings_live.ex:57
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "New password"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/components/layouts/root.html.heex:33
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Settings"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/form_component.ex:26
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Date"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/form_component.ex:25
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Description"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/form_component.ex:24
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Subject"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/form_component.ex:13
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Use this form to manage exam records in your database."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/index.html.heex:2
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Listing Exams"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/form_component.ex:28
|
||||||
|
#, elixir-autogen, elixir-format, fuzzy
|
||||||
|
msgid "Save Exam"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/form_component.ex:28
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Saving..."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/index.html.heex:40
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Yesterday"
|
||||||
|
msgid_plural "%{count} days passed"
|
||||||
|
msgstr[0] ""
|
||||||
|
msgstr[1] ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/index.html.heex:38
|
||||||
|
#, elixir-autogen, elixir-format, fuzzy
|
||||||
|
msgid "Tommorow"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/components/layouts/root.html.heex:59
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Log in"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/components/layouts/root.html.heex:51
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Register"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/index.html.heex:85
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Fri"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/index.html.heex:84
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Mon"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/index.html.heex:85
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Sat"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/index.html.heex:84
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Sun"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/index.html.heex:84
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Thu"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/index.html.heex:84
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Tue"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/index.html.heex:84
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Wed"
|
||||||
|
msgstr ""
|
BIN
priv/gettext/ru/LC_MESSAGES/default.mo
Normal file
BIN
priv/gettext/ru/LC_MESSAGES/default.mo
Normal file
Binary file not shown.
242
priv/gettext/ru/LC_MESSAGES/default.po
Normal file
242
priv/gettext/ru/LC_MESSAGES/default.po
Normal file
|
@ -0,0 +1,242 @@
|
||||||
|
# # This file is a PO Template file.
|
||||||
|
# #
|
||||||
|
# # "msgid"s here are often extracted from source code.
|
||||||
|
# # Add new messages manually only if they're dynamic
|
||||||
|
# # messages that can't be statically extracted.
|
||||||
|
# #
|
||||||
|
# # Run "mix gettext.extract" to bring this file up to
|
||||||
|
# # date. Leave "msgstr"s empty as changing them here has no
|
||||||
|
# # effect: edit them in PO (.po) files instead.
|
||||||
|
# Vladimir Rubin <vavakado@proton.me>, 2024.
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: unnamed project\n"
|
||||||
|
"POT-Creation-Date: \n"
|
||||||
|
"PO-Revision-Date: 2024-12-23 19:01+0200\n"
|
||||||
|
"Last-Translator: Vladimir Rubin <vavakado@proton.me>\n"
|
||||||
|
"Language-Team: Russian\n"
|
||||||
|
"Language: ru\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"X-Generator: Gtranslator 47.1\n"
|
||||||
|
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
|
||||||
|
"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
|
||||||
|
|
||||||
|
#: lib/exmr_web/controllers/page_html/home.html.heex:51
|
||||||
|
msgid "A simple, modern, and fast exam management system."
|
||||||
|
msgstr "Простая, современная и быстрая система управления экзаменами."
|
||||||
|
|
||||||
|
#: lib/exmr_web/components/core_components.ex:489
|
||||||
|
msgid "Actions"
|
||||||
|
msgstr "Действия"
|
||||||
|
|
||||||
|
#: lib/exmr_web/components/core_components.ex:164
|
||||||
|
msgid "Attempting to reconnect"
|
||||||
|
msgstr "Попытка восстановить соединение"
|
||||||
|
|
||||||
|
#: lib/exmr_web/controllers/page_html/home.html.heex:54
|
||||||
|
msgid "Built using Phoenix LiveView, Ecto, and TailwindCSS by @vavakado"
|
||||||
|
msgstr ""
|
||||||
|
"Создано с использованием Phoenix LiveView, Ecto и TailwindCSS в исполнении "
|
||||||
|
"@vavakado"
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/index.html.heex:50
|
||||||
|
msgid "Edit"
|
||||||
|
msgstr "Изменить"
|
||||||
|
|
||||||
|
#: lib/exmr_web/components/core_components.ex:155
|
||||||
|
msgid "Error!"
|
||||||
|
msgstr "Ошибка!"
|
||||||
|
|
||||||
|
#: lib/exmr_web/controllers/page_html/home.html.heex:96
|
||||||
|
msgid "Exams"
|
||||||
|
msgstr "Экзамены"
|
||||||
|
|
||||||
|
#: lib/exmr_web/components/core_components.ex:176
|
||||||
|
msgid "Hang in there while we get back on track"
|
||||||
|
msgstr "Держитесь, пока мы не вернемся в строй"
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/index.ex:34
|
||||||
|
#: lib/exmr_web/live/exam_live/index.html.heex:23
|
||||||
|
msgid "New Exam"
|
||||||
|
msgstr "Новый экзамен"
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/index.html.heex:57
|
||||||
|
msgid "Remove"
|
||||||
|
msgstr "Удалить"
|
||||||
|
|
||||||
|
#: lib/exmr_web/components/core_components.ex:171
|
||||||
|
msgid "Something went wrong!"
|
||||||
|
msgstr "Что-то пошло не так!"
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/index.html.heex:17
|
||||||
|
msgid "Sort by Date"
|
||||||
|
msgstr "Сортировать по дате"
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/index.html.heex:9
|
||||||
|
msgid "Sort by Subject"
|
||||||
|
msgstr "Сортировать по предмету"
|
||||||
|
|
||||||
|
#: lib/exmr_web/components/core_components.ex:154
|
||||||
|
msgid "Success!"
|
||||||
|
msgstr "Успех!"
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/index.html.heex:37
|
||||||
|
msgid "Today"
|
||||||
|
msgstr "Сегодня"
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/index.html.heex:39
|
||||||
|
msgid "Tomorrow"
|
||||||
|
msgid_plural "%{count} days left"
|
||||||
|
msgstr[0] "%{count} день остался"
|
||||||
|
msgstr[1] "%{count} дня осталось"
|
||||||
|
msgstr[2] "%{count} дней осталось"
|
||||||
|
|
||||||
|
#: lib/exmr_web/components/core_components.ex:159
|
||||||
|
msgid "We can't find the internet"
|
||||||
|
msgstr "Мы не можем найти интернет"
|
||||||
|
|
||||||
|
#: lib/exmr_web/components/core_components.ex:80
|
||||||
|
#: lib/exmr_web/components/core_components.ex:134
|
||||||
|
msgid "close"
|
||||||
|
msgstr "закрыть"
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/user_settings_live.ex:139
|
||||||
|
msgid "A link to confirm your email change has been sent to the new address."
|
||||||
|
msgstr ""
|
||||||
|
"На новый адрес отправлена ссылка для подтверждения изменения электронной "
|
||||||
|
"почты."
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/user_settings_live.ex:9
|
||||||
|
msgid "Account Settings"
|
||||||
|
msgstr "Настройки аккаунта"
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/user_settings_live.ex:33
|
||||||
|
msgid "Change Email"
|
||||||
|
msgstr "Изменить электронную почту"
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/user_settings_live.ex:76
|
||||||
|
msgid "Change Password"
|
||||||
|
msgstr "Изменить пароль"
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/user_settings_live.ex:32
|
||||||
|
#: lib/exmr_web/live/user_settings_live.ex:75
|
||||||
|
msgid "Changing..."
|
||||||
|
msgstr "Меняется..."
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/user_settings_live.ex:63
|
||||||
|
msgid "Confirm new password"
|
||||||
|
msgstr "Подтвердите новый пароль"
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/user_settings_live.ex:27
|
||||||
|
#: lib/exmr_web/live/user_settings_live.ex:69
|
||||||
|
msgid "Current password"
|
||||||
|
msgstr "Текущий пароль"
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/user_settings_live.ex:21
|
||||||
|
msgid "Email"
|
||||||
|
msgstr "Электронная почта"
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/user_settings_live.ex:92
|
||||||
|
msgid "Email change link is invalid or it has expired."
|
||||||
|
msgstr ""
|
||||||
|
"Ссылка для изменения электронной почты недействительна или срок ее действия "
|
||||||
|
"истек."
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/user_settings_live.ex:89
|
||||||
|
msgid "Email changed successfully."
|
||||||
|
msgstr "Электронная почта успешно изменена."
|
||||||
|
|
||||||
|
#: lib/exmr_web/components/layouts/root.html.heex:42
|
||||||
|
msgid "Log out"
|
||||||
|
msgstr "Выйти из системы"
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/user_settings_live.ex:10
|
||||||
|
msgid "Manage your account email address and password settings"
|
||||||
|
msgstr "Управление адресом электронной почты и настройками пароля"
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/user_settings_live.ex:57
|
||||||
|
msgid "New password"
|
||||||
|
msgstr "Новый пароль"
|
||||||
|
|
||||||
|
#: lib/exmr_web/components/layouts/root.html.heex:33
|
||||||
|
msgid "Settings"
|
||||||
|
msgstr "Настройки"
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/form_component.ex:26
|
||||||
|
msgid "Date"
|
||||||
|
msgstr "Дата"
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/form_component.ex:25
|
||||||
|
msgid "Description"
|
||||||
|
msgstr "Описание"
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/form_component.ex:24
|
||||||
|
msgid "Subject"
|
||||||
|
msgstr "Предмет"
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/form_component.ex:13
|
||||||
|
msgid "Use this form to manage exam records in your database."
|
||||||
|
msgstr ""
|
||||||
|
"Используйте эту форму для управления записями экзаменов в вашей базе данных."
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/index.html.heex:2
|
||||||
|
msgid "Listing Exams"
|
||||||
|
msgstr "Список Экзаменов"
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/form_component.ex:28
|
||||||
|
msgid "Save Exam"
|
||||||
|
msgstr "Сохранить экзамен"
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/form_component.ex:28
|
||||||
|
msgid "Saving..."
|
||||||
|
msgstr "Сохранение..."
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/index.html.heex:40
|
||||||
|
msgid "Yesterday"
|
||||||
|
msgid_plural "%{count} days passed"
|
||||||
|
msgstr[0] "%{count} день прошёл"
|
||||||
|
msgstr[1] "%{count} дня прошло"
|
||||||
|
msgstr[2] "%{count} дней прошло"
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/index.html.heex:38
|
||||||
|
msgid "Tommorow"
|
||||||
|
msgstr "Завтра"
|
||||||
|
|
||||||
|
#: lib/exmr_web/components/layouts/root.html.heex:59
|
||||||
|
msgid "Log in"
|
||||||
|
msgstr "Зайти в аккаунт"
|
||||||
|
|
||||||
|
#: lib/exmr_web/components/layouts/root.html.heex:51
|
||||||
|
msgid "Register"
|
||||||
|
msgstr "Зарегестрироваться"
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/index.html.heex:85
|
||||||
|
msgid "Fri"
|
||||||
|
msgstr "Пт"
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/index.html.heex:84
|
||||||
|
msgid "Mon"
|
||||||
|
msgstr "Пн"
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/index.html.heex:85
|
||||||
|
msgid "Sat"
|
||||||
|
msgstr "Сб"
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/index.html.heex:84
|
||||||
|
msgid "Sun"
|
||||||
|
msgstr "Вс"
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/index.html.heex:84
|
||||||
|
msgid "Thu"
|
||||||
|
msgstr "Чт"
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/index.html.heex:84
|
||||||
|
msgid "Tue"
|
||||||
|
msgstr "Вт"
|
||||||
|
|
||||||
|
#: lib/exmr_web/live/exam_live/index.html.heex:84
|
||||||
|
msgid "Wed"
|
||||||
|
msgstr "Ср"
|
122
priv/gettext/ru/LC_MESSAGES/errors.po
Normal file
122
priv/gettext/ru/LC_MESSAGES/errors.po
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
# # "msgid"s in this file come from POT (.pot) files.
|
||||||
|
# ##
|
||||||
|
# ## Do not add, change, or remove "msgid"s manually here as
|
||||||
|
# ## they're tied to the ones in the corresponding POT file
|
||||||
|
# ## (with the same domain).
|
||||||
|
# ##
|
||||||
|
# ## Use "mix gettext.extract --merge" or "mix gettext.merge"
|
||||||
|
# ## to merge POT files into PO files.
|
||||||
|
# Vladimir Rubin <vavakado@proton.me>, 2024.
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Language: ru\n"
|
||||||
|
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
|
||||||
|
"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Project-Id-Version: unnamed project\n"
|
||||||
|
"Last-Translator: Vladimir Rubin <vavakado@proton.me>\n"
|
||||||
|
"Language-Team: Russian\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"PO-Revision-Date: 2024-12-12 18:52+0200\n"
|
||||||
|
"X-Generator: Gtranslator 47.1\n"
|
||||||
|
|
||||||
|
msgid "can't be blank"
|
||||||
|
msgstr "не может быть пустым"
|
||||||
|
|
||||||
|
msgid "has already been taken"
|
||||||
|
msgstr "уже забрали"
|
||||||
|
|
||||||
|
msgid "is invalid"
|
||||||
|
msgstr "недействителен"
|
||||||
|
|
||||||
|
msgid "must be accepted"
|
||||||
|
msgstr "должен быть принят"
|
||||||
|
|
||||||
|
msgid "has invalid format"
|
||||||
|
msgstr "имеет неправильный формат"
|
||||||
|
|
||||||
|
msgid "has an invalid entry"
|
||||||
|
msgstr "имеет недопустимую запись"
|
||||||
|
|
||||||
|
msgid "is reserved"
|
||||||
|
msgstr "зарезервирован"
|
||||||
|
|
||||||
|
msgid "does not match confirmation"
|
||||||
|
msgstr "не соответствует подтверждению"
|
||||||
|
|
||||||
|
msgid "is still associated with this entry"
|
||||||
|
msgstr "по-прежнему связана с этой записью"
|
||||||
|
|
||||||
|
msgid "are still associated with this entry"
|
||||||
|
msgstr "по-прежнему связаны с этой записью"
|
||||||
|
|
||||||
|
msgid "should have %{count} item(s)"
|
||||||
|
msgid_plural "should have %{count} item(s)"
|
||||||
|
msgstr[0] "должен иметь %{count} элементов"
|
||||||
|
msgstr[1] "должен иметь %{count} элемента"
|
||||||
|
msgstr[2] "должен иметь %{count} элементов"
|
||||||
|
|
||||||
|
msgid "should be %{count} character(s)"
|
||||||
|
msgid_plural "should be %{count} character(s)"
|
||||||
|
msgstr[0] "должен быть %{count} символов"
|
||||||
|
msgstr[1] "должен быть %{count} символа"
|
||||||
|
msgstr[2] "должен быть %{count} символов"
|
||||||
|
|
||||||
|
msgid "should be %{count} byte(s)"
|
||||||
|
msgid_plural "should be %{count} byte(s)"
|
||||||
|
msgstr[0] "должно быть %{count} байт"
|
||||||
|
msgstr[1] "должно быть %{count} байта"
|
||||||
|
msgstr[2] "должно быть %{count} байтов"
|
||||||
|
|
||||||
|
msgid "should have at least %{count} item(s)"
|
||||||
|
msgid_plural "should have at least %{count} item(s)"
|
||||||
|
msgstr[0] "должно быть не менее %{count} элементов"
|
||||||
|
msgstr[1] "должно быть не менее %{count} элемента"
|
||||||
|
msgstr[2] "должно быть не менее %{count} элементов"
|
||||||
|
|
||||||
|
msgid "should be at least %{count} character(s)"
|
||||||
|
msgid_plural "should be at least %{count} character(s)"
|
||||||
|
msgstr[0] "должно быть не менее %{count} символов"
|
||||||
|
msgstr[1] "должно быть не менее %{count} символа"
|
||||||
|
msgstr[2] "должно быть не менее %{count} символов"
|
||||||
|
|
||||||
|
msgid "should be at least %{count} byte(s)"
|
||||||
|
msgid_plural "should be at least %{count} byte(s)"
|
||||||
|
msgstr[0] "должно быть хотя бы %{count} байт"
|
||||||
|
msgstr[1] "должно быть хотя бы %{count} байта"
|
||||||
|
msgstr[2] "должно быть хотя бы %{count} байтов"
|
||||||
|
|
||||||
|
msgid "should have at most %{count} item(s)"
|
||||||
|
msgid_plural "should have at most %{count} item(s)"
|
||||||
|
msgstr[0] "должно содержать не более %{count} элементов"
|
||||||
|
msgstr[1] "должно содержать не более %{count} элементов"
|
||||||
|
msgstr[2] "должно содержать не более %{count} элементов"
|
||||||
|
|
||||||
|
msgid "should be at most %{count} character(s)"
|
||||||
|
msgid_plural "should be at most %{count} character(s)"
|
||||||
|
msgstr[0] "должно быть не более %{count} символа(ов)"
|
||||||
|
msgstr[1] "должно быть не более %{count} символа(ов)"
|
||||||
|
msgstr[2] "должно быть не более %{count} символа(ов)"
|
||||||
|
|
||||||
|
msgid "should be at most %{count} byte(s)"
|
||||||
|
msgid_plural "should be at most %{count} byte(s)"
|
||||||
|
msgstr[0] "должно быть не более %{count} байт(ов)."
|
||||||
|
msgstr[1] "должно быть не более %{count} байт(ов)."
|
||||||
|
msgstr[2] "должно быть не более %{count} байт(ов)."
|
||||||
|
|
||||||
|
msgid "must be less than %{number}"
|
||||||
|
msgstr "должно быть меньше, чем %{number}"
|
||||||
|
|
||||||
|
msgid "must be greater than %{number}"
|
||||||
|
msgstr "должно быть больше, чем %{number}"
|
||||||
|
|
||||||
|
msgid "must be less than or equal to %{number}"
|
||||||
|
msgstr "должно быть меньше или равно %{number}"
|
||||||
|
|
||||||
|
msgid "must be greater than or equal to %{number}"
|
||||||
|
msgstr "должно быть больше или равно %{number}"
|
||||||
|
|
||||||
|
msgid "must be equal to %{number}"
|
||||||
|
msgstr "должно быть равно %{number}"
|
Loading…
Reference in a new issue