145 lines
6.0 KiB
Erlang
145 lines
6.0 KiB
Erlang
-module(admin_handler_calendars).
|
|
-behaviour(cowboy_handler).
|
|
-export([init/2]).
|
|
-export([trails/0]).
|
|
-export([calendar_schema/0]).
|
|
|
|
-include("records.hrl").
|
|
|
|
init(Req, _Opts) ->
|
|
case cowboy_req:method(Req) of
|
|
<<"GET">> -> list_calendars(Req);
|
|
_ -> handler_utils:send_error(Req, 405, <<"Method not allowed">>)
|
|
end.
|
|
|
|
trails() ->
|
|
[
|
|
#{
|
|
path => <<"/v1/admin/calendars">>,
|
|
method => <<"GET">>,
|
|
description => <<"List all calendars (admin)">>,
|
|
tags => [<<"Admin Calendars">>],
|
|
parameters => [
|
|
#{name => <<"status">>, in => <<"query">>, schema => #{type => string}, description => <<"Filter by status">>},
|
|
#{name => <<"type">>, in => <<"query">>, schema => #{type => string}, description => <<"Filter by type">>},
|
|
#{name => <<"q">>, in => <<"query">>, schema => #{type => string}, description => <<"Search in title/description">>},
|
|
#{name => <<"sort_by">>, in => <<"query">>, schema => #{type => string}, description => <<"Sort field (title, created_at, updated_at)">>},
|
|
#{name => <<"sort_order">>, in => <<"query">>, schema => #{type => string, enum => [<<"asc">>, <<"desc">>]}, description => <<"Sort direction">>},
|
|
#{name => <<"limit">>, in => <<"query">>, schema => #{type => integer}, description => <<"Page size">>},
|
|
#{name => <<"offset">>, in => <<"query">>, schema => #{type => integer}, description => <<"Offset">>}
|
|
],
|
|
responses => #{
|
|
200 => #{
|
|
description => <<"Array of calendars">>,
|
|
content => #{<<"application/json">> => #{
|
|
schema => #{
|
|
type => array,
|
|
items => calendar_schema()
|
|
}
|
|
}}
|
|
},
|
|
403 => #{description => <<"Forbidden">>}
|
|
}
|
|
}
|
|
].
|
|
|
|
calendar_schema() ->
|
|
#{
|
|
type => object,
|
|
properties => #{
|
|
id => #{type => string},
|
|
owner_id => #{type => string},
|
|
title => #{type => string},
|
|
description => #{type => string},
|
|
short_name => #{type => string},
|
|
category => #{type => string},
|
|
color => #{type => string},
|
|
image_url => #{type => string},
|
|
settings => #{type => object},
|
|
tags => #{type => array, items => #{type => string}},
|
|
type => #{type => string, enum => [<<"personal">>, <<"commercial">>]},
|
|
confirmation => #{type => string},
|
|
rating_avg => #{type => number},
|
|
rating_count => #{type => integer},
|
|
status => #{type => string, enum => [<<"active">>, <<"frozen">>, <<"deleted">>]},
|
|
reason => #{type => string},
|
|
created_at => #{type => string, format => date_time},
|
|
updated_at => #{type => string, format => date_time}
|
|
}
|
|
}.
|
|
|
|
%%% HTTP-метод
|
|
|
|
list_calendars(Req) ->
|
|
case handler_utils:auth_admin(Req) of
|
|
{ok, _AdminId, Req1} ->
|
|
Pagination = handler_utils:parse_pagination_params(Req1),
|
|
Filters = parse_calendar_filters(Req1),
|
|
{ok, AllCalendars} = logic_calendar:admin_list_all(),
|
|
Filtered = apply_calendar_filters(AllCalendars, Filters),
|
|
% Формируем параметры сортировки из query-параметров
|
|
Qs = cowboy_req:parse_qs(Req1),
|
|
SortBy = proplists:get_value(<<"sort_by">>, Qs, <<"created_at">>),
|
|
SortOrder = proplists:get_value(<<"sort_order">>, Qs, <<"desc">>),
|
|
Sorted = sort_calendars(Filtered, SortBy, SortOrder),
|
|
Total = length(Sorted),
|
|
Page = lists:sublist(Sorted, maps:get(offset, Pagination) + 1, maps:get(limit, Pagination)),
|
|
Json = [handler_utils:calendar_to_json(C) || C <- Page],
|
|
ExtraHeaders = handler_utils:pagination_headers(Pagination, Total),
|
|
handler_utils:send_json(Req1, 200, Json, ExtraHeaders);
|
|
{error, Code, Msg, Req1} ->
|
|
handler_utils:send_error(Req1, Code, Msg)
|
|
end.
|
|
|
|
%%% Фильтрация
|
|
|
|
parse_calendar_filters(Req) ->
|
|
Qs = cowboy_req:parse_qs(Req),
|
|
#{
|
|
status => proplists:get_value(<<"status">>, Qs),
|
|
type => proplists:get_value(<<"type">>, Qs),
|
|
q => proplists:get_value(<<"q">>, Qs)
|
|
}.
|
|
|
|
apply_calendar_filters(Calendars, Filters) ->
|
|
StatusBin = maps:get(status, Filters),
|
|
TypeBin = maps:get(type, Filters),
|
|
Q = maps:get(q, Filters),
|
|
|
|
F1 = case StatusBin of
|
|
undefined -> Calendars;
|
|
_ -> Status = binary_to_existing_atom(StatusBin, utf8),
|
|
[C || C <- Calendars, C#calendar.status =:= Status]
|
|
end,
|
|
F2 = case TypeBin of
|
|
undefined -> F1;
|
|
_ -> Type = binary_to_existing_atom(TypeBin, utf8),
|
|
[C || C <- F1, C#calendar.type =:= Type]
|
|
end,
|
|
case Q of
|
|
undefined -> F2;
|
|
_ ->
|
|
QLower = string:lowercase(binary_to_list(Q)),
|
|
[C || C <- F2,
|
|
string:str(string:lowercase(binary_to_list(C#calendar.title)), QLower) > 0 orelse
|
|
string:str(string:lowercase(binary_to_list(C#calendar.description)), QLower) > 0]
|
|
end.
|
|
|
|
%%% Сортировка
|
|
|
|
sort_calendars(Calendars, SortBy, SortOrder) ->
|
|
Index = case SortBy of
|
|
<<"title">> -> #calendar.title; % элемент 3
|
|
<<"created_at">> -> #calendar.created_at; % элемент 17
|
|
<<"updated_at">> -> #calendar.updated_at; % элемент 18
|
|
_ -> #calendar.created_at
|
|
end,
|
|
Sorted = lists:sort(fun(A, B) ->
|
|
VA = element(Index, A),
|
|
VB = element(Index, B),
|
|
case SortOrder of
|
|
<<"desc">> -> VA > VB;
|
|
_ -> VA < VB
|
|
end
|
|
end, Calendars),
|
|
Sorted. |