EZCalendar
Turn your Ecto queries into calendar style data structures!
- Includes view helpers for server side rendering in Phoenix
- Tested with Ecto, Calecto and TimexEcto
Installation
This package is available in Hex.
Add ez_calendar
to your mix.exs
file.
def deps do
[{:ez_calendar, "~> 0.1.0"}]
end
def application do
[applications: [:ez_calendar]]
end
Add EZCalendar to your repo.ex
:
defmodule MyApp.Repo do
use Ecto.Repo, otp_app: :my_app
use EZCalendar
end
Usage
Turn your queries into calendar structs
def calendar(params \\ DateTime.utc_now) do
MyApp.Shift
|> with_employee
|> MyApp.Repo.month_calendar(params)
end
Month, week, day and biweekly calendars exist and can be called with their respective functions or by passing the module in as an argument to Repo.calendar/4
.
All Repo functions have a bang! variant that will raise an error rather than returning a tuple.
The params can be a Date, DateTime, erl style tuple or map containing the keys day
month
and year
.
The day can be ommited from the params, doing so will result in the first of the month being used.
Tuple and map values will be cast as integers for easy integration with phoenix.
Repo.day_calendar(query, %{day: 1, month: 11, year: 2016})
Repo.week_calendar(query, {2016, 11, 27})
Repo.month_calendar(query, {2016, 11})
Repo.biweekly_calendar(query, {2016, 11, 3})
# Custom calendar modules can be defined.
Repo.calendar(query, MyApp.PayrollCalendar, params)
# Examples lib/ez_calendar/calendars/
# Behaviour lib/ez_calendar/calendars/calendar.ex
The timezone and query field can be set with :tz
and :field
as options.
If using a biweekly calendar the biweekly_start
can also be set.
Default values for the options can be set in the config.
Repo.biweekly_calendar(
query,
params,
field: [:starting, :ending],
tz: "America/Vancouver",
biweekly_start: {2016, 1, 4}
)
The field
option represents field name in your schema used to build the calendars.
It can be a single field, or 2 item list or tuple representing a start/end range.
Date or DateTime types are valid, if using a range the types must match.
The timezone defaults to UTC
and must a be valid TZ data format. It’s used to appropriately set each dates today?
boolean flag.
The biweekly_start
must be an erl equal to the first day in any of the two week periods you want the calendar to represent.
Can start on any day of the week.
Multiple Queries
You can use a keyword list for the query. Each query will use the default field(s) unless provided in the query list or in the options.
[
shifts: [Shift, [:starting, :ending]],
events: [Event, :starting_at],
meetings: Meeting,
deliveries: Delivery
]
|> Repo.month_calendar({2016, 11}, field: :scheduled_for)
Query results will be accessible by their key in each dates data field:
[
# ...
%{
data: %{
shifts: [%Shift{}, %Shift{}],
events: [%Event{}],
meetings: [],
deliveries: [%Delivery{}]
},
day: 18,
month: 11,
today?: true,
weekday: "Friday",
year: 2016
},
# ...
]
Phoenix
Install one of the stylesheet formats.
$ mix ez_calendar.css
$ mix ez_calendar.scss
$ mix ez_calendar.sass
Build a calendar from the params.
def index(conn, params) do
case MyApp.Repo.month_calendar(MyApp.Shift, params) do
{:ok, calendar} ->
render(conn, "index.html", calendar: calendar)
{:error, reason} ->
calendar = MyApp.Repo.month_calendar!(MyApp.Shift, DateTime.utc_now)
conn
|> put_flash(:error, reason)
|> render("index.html", calendar: calendar)
end
end
Import the view helpers.
defmodule MyApp.ShiftView do
use MyApp.Web, :view
import EZCalendar.HTML
end
Render the calendar with the view helpers.
<%= calendar_prev @calendar, "/shifts/:year/:month" %>
<%= @calendar.title %>
<%= calendar_next @calendar, "/shifts/:year/:month" %>
<%= render_calendar @calendar, fn(date)-> %>
<!-- calendar date -->
<%= for shift <- date.data do %>
<!-- query results for date -->
<% end %>
<% end %>
If you want to use an html module that is not the default for that calendar,
you can also use the render_calendar/3
function to pass in the module as the first arugument.
<%= render_calendar MyApp.MonthCalendarHTML, @calendar, fn(date)-> %>
<% end %>
# Custom HTML modules can be defined.
# Examples lib/ez_calendar/html/calendars/
# Behaviour lib/ez_calendar/html/calendars/calendar.ex
The calendar_next
and calendar_prev
functions accept the
calendar struct and a string showing how to format the path.
The correct parameters will replace the :day
, :month
and :year
placeholders.
The links contents can also be passed in as an optional third argument.
<%= calendar_next @calendar, "/shifts/:year/:month/:day", "Next" %>
<%= calendar_next @calendar, "/shifts/:year/:month", fn()-> %>
<!-- link content -->
<% end %>
<%= calendar_next @calendar, "/shifts?year=:year&month=:month", "Next" %>
Configuration
The following values are the defaults.
config :ez_calendar,
default_field: :date,
default_tz: "UTC",
default_next: ">",
default_prev: "<",
default_biweekly_start: {2016, 1, 3}
Contributing
Help is very welcome! Please make sure all the tests are passing before submitting a pull request.
Setup the database
$ MIX_ENV=test mix ecto.create && mix ecto.migrate
Run the tests!
$ mix test