updated npm dependencies and implemented basic import
This commit is contained in:
parent
686bb489ba
commit
1d017a3cd1
6 changed files with 925 additions and 618 deletions
|
@ -57,6 +57,8 @@ import "../node_modules/bootstrap-icons/icons/asterisk.svg";
|
||||||
import "../node_modules/bootstrap-icons/icons/card-list.svg";
|
import "../node_modules/bootstrap-icons/icons/card-list.svg";
|
||||||
import "../node_modules/bootstrap-icons/icons/file-earmark-spreadsheet.svg";
|
import "../node_modules/bootstrap-icons/icons/file-earmark-spreadsheet.svg";
|
||||||
import "../node_modules/bootstrap-icons/icons/box-arrow-in-left.svg";
|
import "../node_modules/bootstrap-icons/icons/box-arrow-in-left.svg";
|
||||||
|
import "../node_modules/bootstrap-icons/icons/link.svg";
|
||||||
|
import "../node_modules/bootstrap-icons/icons/link-45deg.svg";
|
||||||
|
|
||||||
// webpack automatically bundles all modules in your
|
// webpack automatically bundles all modules in your
|
||||||
// entry points. Those entry points can be configured
|
// entry points. Those entry points can be configured
|
||||||
|
|
1385
assets/package-lock.json
generated
1385
assets/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -10,7 +10,7 @@
|
||||||
"@fontsource/lato": "^4.2.1",
|
"@fontsource/lato": "^4.2.1",
|
||||||
"@mdi/svg": "^5.9.55",
|
"@mdi/svg": "^5.9.55",
|
||||||
"@popperjs/core": "^2.8.4",
|
"@popperjs/core": "^2.8.4",
|
||||||
"bootstrap": "^5.0.0-beta2",
|
"bootstrap": "^5.0.0-beta3",
|
||||||
"bootstrap-icons": "^1.4.0",
|
"bootstrap-icons": "^1.4.0",
|
||||||
"hamburgers": "^1.1.3",
|
"hamburgers": "^1.1.3",
|
||||||
"heroicons": "^0.4.2",
|
"heroicons": "^0.4.2",
|
||||||
|
|
|
@ -1,55 +1,94 @@
|
||||||
defmodule Shift73kWeb.ShiftImportLive.Index do
|
defmodule Shift73kWeb.ShiftImportLive.Index do
|
||||||
use Shift73kWeb, :live_view
|
use Shift73kWeb, :live_view
|
||||||
|
|
||||||
|
alias Shift73k.Repo
|
||||||
|
alias Shift73k.Shifts
|
||||||
|
|
||||||
|
@url_regex_str "[-a-zA-Z0-9@:%._\\+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6}\\b([-a-zA-Z0-9()@:%_\\+.~#?&//=]*)"
|
||||||
|
@url_regex Regex.compile!(@url_regex_str)
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def mount(_params, session, socket) do
|
def mount(_params, session, socket) do
|
||||||
HTTPoison.start()
|
HTTPoison.start()
|
||||||
|
|
||||||
socket
|
socket
|
||||||
|> assign_defaults(session)
|
|> assign_defaults(session)
|
||||||
|
|> assign(:url_valid, :false)
|
||||||
|
|> assign(:url_validated, :false)
|
||||||
|
|> assign(:tz_valid, :true)
|
||||||
|> live_okreply()
|
|> live_okreply()
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def handle_event("save", %{"ics_import" => %{"ics_url" => ics_url}}, socket) do
|
def handle_event("validate", %{"ics_import" => %{"ics_url" => url, "time_zone" => tz}}, socket) do
|
||||||
ics_url
|
socket
|
||||||
|> IO.inspect(label: "given ical url :")
|
|> assign(:url_valid, Regex.match?(@url_regex, url) |> IO.inspect(label: "url valid?"))
|
||||||
|> HTTPoison.get!()
|
|> assign(:url_validated, true)
|
||||||
|> handle_http_ics_response(socket)
|
|> assign(:tz_valid, Enum.member?(Tzdata.zone_list(), tz))
|
||||||
|
|> live_noreply()
|
||||||
end
|
end
|
||||||
|
|
||||||
defp handle_http_ics_response(%HTTPoison.Response{status_code: 200} = resp, socket) do
|
|
||||||
|
@impl true
|
||||||
|
def handle_event("save", %{"ics_import" => %{"ics_url" => url, "time_zone" => tz}}, socket) do
|
||||||
|
url
|
||||||
|
|> HTTPoison.get!()
|
||||||
|
|> handle_http_ics_response(socket, tz)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp handle_http_ics_response(%HTTPoison.Response{status_code: 200} = resp, socket, tz) do
|
||||||
case content_type_calendar?(resp.headers) do
|
case content_type_calendar?(resp.headers) do
|
||||||
false ->
|
false ->
|
||||||
handle_http_ics_response(false, socket)
|
handle_http_ics_response(false, socket, tz)
|
||||||
|
|
||||||
true ->
|
true ->
|
||||||
resp.body
|
resp.body
|
||||||
|> ICalendar.from_ics()
|
|> ICalendar.from_ics()
|
||||||
|> handle_parsed_ics_data(socket)
|
|> handle_parsed_ics_data(socket, tz)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp handle_http_ics_response(_, socket) do
|
defp handle_http_ics_response(_, socket, _tz) do
|
||||||
socket
|
socket
|
||||||
|> put_flash(:error, "Bad data, bad URL, or some other error")
|
|> put_flash(:error, "Bad data, bad URL, or some other error. Page the dev!")
|
||||||
|> live_noreply()
|
|> live_noreply()
|
||||||
end
|
end
|
||||||
|
|
||||||
defp handle_parsed_ics_data([], socket), do: handle_http_ics_response(false, socket)
|
defp handle_parsed_ics_data([], socket, tz), do: handle_http_ics_response(false, socket, tz)
|
||||||
|
|
||||||
defp handle_parsed_ics_data(events, socket) do
|
defp handle_parsed_ics_data(events, socket, tz) do
|
||||||
IO.inspect(events, label: "We got some ical events! :")
|
IO.inspect(events, label: "We got some ical events! :")
|
||||||
|
IO.inspect(tz, label: "time zone was :")
|
||||||
|
|
||||||
socket
|
to_insert =
|
||||||
|> put_flash(:success, "We got some ical events")
|
events
|
||||||
|> live_noreply()
|
|> Stream.map(&shift_from_event(&1, tz, socket.assigns.current_user.id))
|
||||||
|
|> Enum.map(&Repo.timestamp/1)
|
||||||
|
|> Shifts.create_multiple()
|
||||||
|
|> handle_create_multiple_result(length(events), socket)
|
||||||
|
|> live_noreply()
|
||||||
end
|
end
|
||||||
|
|
||||||
def ical_request(ics_url) do
|
defp handle_create_multiple_result(result, event_count, socket) do
|
||||||
# ics_url = "https://calendar.google.com/calendar/ical/l44mcggj2rsoqq7prlakvitqfo%40group.calendar.google.com/private-66f4cf8b340bdd6e9de8c60b2ae36528/basic.ics"
|
{status, msg} =
|
||||||
|
case result do
|
||||||
|
{:error, errmsg} ->
|
||||||
|
{:error, "Ope, problem error inserting shifts, page the dev! Message: #{errmsg}"}
|
||||||
|
|
||||||
|
{n, _} ->
|
||||||
|
s = (n > 1 && "s") || ""
|
||||||
|
|
||||||
|
if n == event_count do
|
||||||
|
{:success, "Successfully imported #{n} event#{s}"}
|
||||||
|
else
|
||||||
|
{:warning, "Some error, only #{n} event#{s} imported but seemed like iCal contained #{event_count}?"}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
put_flash(socket, status, msg)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
defp content_type_calendar?(headers) do
|
defp content_type_calendar?(headers) do
|
||||||
headers
|
headers
|
||||||
|> List.keyfind("Content-Type", 0)
|
|> List.keyfind("Content-Type", 0)
|
||||||
|
@ -57,7 +96,34 @@ defmodule Shift73kWeb.ShiftImportLive.Index do
|
||||||
|> String.contains?("text/calendar")
|
|> String.contains?("text/calendar")
|
||||||
end
|
end
|
||||||
|
|
||||||
def shift_from_event(%ICalendar.Event{} = event) do
|
defp shift_from_event(%ICalendar.Event{} = e, tz, user_id) do
|
||||||
%{}
|
dtzstart = DateTime.shift_zone!(e.dtstart, tz)
|
||||||
|
dtzend = DateTime.add(dtzstart, DateTime.diff(e.dtend, e.dtstart))
|
||||||
|
|
||||||
|
%{
|
||||||
|
subject: e.summary,
|
||||||
|
location: e.location,
|
||||||
|
description: fix_description(e.description),
|
||||||
|
date: DateTime.to_date(dtzstart),
|
||||||
|
time_zone: tz,
|
||||||
|
time_start: DateTime.to_time(dtzstart),
|
||||||
|
time_end: DateTime.to_time(dtzend),
|
||||||
|
user_id: user_id
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp fix_description(description) do
|
||||||
|
description
|
||||||
|
|> String.replace("<br>", "\r\n")
|
||||||
|
|> String.replace("<br\\>", "\r\n")
|
||||||
|
|> String.replace("<br \\>", "\r\n")
|
||||||
|
|> String.replace("<b>", "**")
|
||||||
|
|> String.replace("</b>", "**")
|
||||||
|
|> String.replace("<strong>", "**")
|
||||||
|
|> String.replace("</strong>", "**")
|
||||||
|
|> String.replace("<i>", "*")
|
||||||
|
|> String.replace("</i>", "*")
|
||||||
|
|> String.replace("<em>", "*")
|
||||||
|
|> String.replace("</em>", "*")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -11,18 +11,53 @@
|
||||||
<div class="row justify-content-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-12 col-sm-11 col-md-10 col-lg-9 col-xxl-8">
|
<div class="col-12 col-sm-11 col-md-10 col-lg-9 col-xxl-8">
|
||||||
|
|
||||||
<%= form_for :ics_import, "#", [phx_submit: "save"], fn iimf -> %>
|
<%= form_for :ics_import, "#", [phx_change: "validate", phx_submit: "save"], fn iimf -> %>
|
||||||
|
|
||||||
<div class="row">
|
<% show_url_error = @url_validated && !@url_valid || false %>
|
||||||
<div class="col mb-3">
|
<% valid_class = @url_validated && "is-valid" || "" %>
|
||||||
<%= label iimf, :ics_url, "iCal/ics URL", class: "form-label" %>
|
<%= label iimf, :ics_url, "iCal/ics URL", class: "form-label" %>
|
||||||
<%= url_input iimf, :ics_url, class: "form-control" %>
|
<div class="inner-addon left-addon mb-3">
|
||||||
|
<%= icon_div @socket, "bi-link", [class: "icon is-left text-muted fs-5"] %>
|
||||||
|
<%= url_input iimf, :ics_url,
|
||||||
|
class: show_url_error && "form-control is-invalid" || "form-control #{valid_class}",
|
||||||
|
phx_debounce: 500,
|
||||||
|
aria_describedby: "ics-import-url-error"
|
||||||
|
%>
|
||||||
|
<%= if show_url_error do %>
|
||||||
|
<div class="invalid-feedback d-block" id="ics-import-url-error">
|
||||||
|
Must be a valid URL
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<% end %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<%= label iimf, :time_zone, class: "form-label" %>
|
||||||
|
<div class="inner-addon left-addon mb-3">
|
||||||
|
<%= icon_div @socket, "bi-map", [class: "icon is-left text-muted fs-5"] %>
|
||||||
|
<%= text_input iimf, :time_zone,
|
||||||
|
value: Shift73k.Util.Dt.app_time_zone(),
|
||||||
|
class: @tz_valid && "form-control" || "form-control is-invalid",
|
||||||
|
phx_debounce: 250,
|
||||||
|
aria_describedby: "ics-import-tz-error",
|
||||||
|
list: "tz_list"
|
||||||
|
%>
|
||||||
|
<datalist id="tz_list">
|
||||||
|
<%= for tz_name <- Tzdata.zone_list() do %>
|
||||||
|
<option value="<%= tz_name %>"></option>
|
||||||
|
<% end %>
|
||||||
|
</datalist>
|
||||||
|
<div class="valid-feedback d-block text-primary">Type to search & select from list of known <%= link "IANA tz database", to: "https://en.wikipedia.org/wiki/List_of_tz_database_time_zones", target: "_blank" %> time zones</div>
|
||||||
|
<%= if !@tz_valid do %>
|
||||||
|
<div class="invalid-feedback d-block" id="ics-import-tz-error">
|
||||||
|
Invalid time zone
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col mb-3 text-end">
|
<div class="col mb-3 text-end">
|
||||||
<%= submit "Submit", class: "btn btn-primary" %>
|
<%= submit "Submit", class: "btn btn-primary", disabled: !@tz_valid || !@url_valid %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -93,7 +93,6 @@
|
||||||
<%= for tz_name <- Tzdata.zone_list() do %>
|
<%= for tz_name <- Tzdata.zone_list() do %>
|
||||||
<option value="<%= tz_name %>"></option>
|
<option value="<%= tz_name %>"></option>
|
||||||
<% end %>
|
<% end %>
|
||||||
end
|
|
||||||
</datalist>
|
</datalist>
|
||||||
<div class="valid-feedback d-block text-primary">Type to search & select from list of known <%= link "IANA tz database", to: "https://en.wikipedia.org/wiki/List_of_tz_database_time_zones", target: "_blank" %> time zones</div>
|
<div class="valid-feedback d-block text-primary">Type to search & select from list of known <%= link "IANA tz database", to: "https://en.wikipedia.org/wiki/List_of_tz_database_time_zones", target: "_blank" %> time zones</div>
|
||||||
<%= error_tag f, :time_zone %>
|
<%= error_tag f, :time_zone %>
|
||||||
|
|
Loading…
Reference in a new issue