Skip to content

Commit

Permalink
Add range picker, more reporting boilerplate (papercups-io#253)
Browse files Browse the repository at this point in the history
  • Loading branch information
reichert621 authored Sep 29, 2020
1 parent e1cf50d commit cb6f070
Show file tree
Hide file tree
Showing 12 changed files with 133 additions and 27 deletions.
11 changes: 10 additions & 1 deletion assets/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -635,13 +635,22 @@ export const enableAccountUser = async (
.then((res) => res.body.data);
};

export const fetchReportingData = async (token = getAccessToken()) => {
type ReportingFilters = {
from_date?: string | null;
to_date?: string | null;
};

export const fetchReportingData = async (
filters = {} as ReportingFilters,
token = getAccessToken()
) => {
if (!token) {
throw new Error('Invalid token!');
}

return request
.get(`/api/reporting`)
.query(filters)
.set('Authorization', token)
.then((res) => res.body.data);
};
10 changes: 10 additions & 0 deletions assets/src/components/DatePicker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import {Dayjs} from 'dayjs';
import dayjsGenerateConfig from 'rc-picker/lib/generate/dayjs';
import generatePicker from 'antd/es/date-picker/generatePicker';
import 'antd/es/date-picker/style/index';

// Ant's DatePicker uses moment.js by default,
// so we need to do this to support dayjs instead
const DatePicker = generatePicker<Dayjs>(dayjsGenerateConfig);

export default DatePicker;
5 changes: 4 additions & 1 deletion assets/src/components/common.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import Alert from 'antd/lib/alert';
import Badge from 'antd/lib/badge';
import Button from 'antd/lib/button';
import Checkbox from 'antd/lib/checkbox';
import DatePicker from 'antd/lib/date-picker';
import Divider from 'antd/lib/divider';
import Drawer from 'antd/lib/drawer';
import Dropdown from 'antd/lib/dropdown';
Expand Down Expand Up @@ -34,8 +33,11 @@ import {
grey,
} from '@ant-design/colors';

import DatePicker from './DatePicker';

const {Title, Text, Paragraph} = Typography;
const {Header, Content, Footer, Sider} = Layout;
const {RangePicker} = DatePicker;

export const colors = {
white: '#fff',
Expand Down Expand Up @@ -103,6 +105,7 @@ export {
Popconfirm,
Popover,
Radio,
RangePicker,
Result,
Select,
Spin,
Expand Down
58 changes: 48 additions & 10 deletions assets/src/components/reporting/ReportingDashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
ResponsiveContainer,
} from 'recharts';
import * as API from '../../api';
import {colors, Alert, Paragraph, Text, Title} from '../common';
import {colors, Alert, Paragraph, RangePicker, Text, Title} from '../common';
import logger from '../../logger';

type ReportingDatum = {
Expand Down Expand Up @@ -156,18 +156,33 @@ const DemoBarChart = ({data}: {data: any}) => {

type Props = {};
type State = {
fromDate: dayjs.Dayjs;
toDate: dayjs.Dayjs;
messagesByDate: Array<DateCount>;
conversationsByDate: Array<DateCount>;
};

class ReportingDashboard extends React.Component<Props, State> {
state: State = {
fromDate: dayjs().subtract(30, 'day'),
toDate: dayjs(),
messagesByDate: [],
conversationsByDate: [],
};

async componentDidMount() {
const data = await API.fetchReportingData();
componentDidMount() {
this.refreshReportingData().catch((err) =>
logger.error('Failed to fetch reporting data:', err)
);
}

refreshReportingData = async () => {
const {fromDate, toDate} = this.state;
const data = await API.fetchReportingData({
from_date: fromDate.toISOString(),
to_date: toDate.toISOString(),
});

logger.debug('Raw reporting data:', data);

this.setState(
Expand All @@ -180,7 +195,7 @@ class ReportingDashboard extends React.Component<Props, State> {
logger.debug('Formatted daily stats:', this.formatDailyStats());
}
);
}
};

groupCountByDate = (data: Array<DateCount>) => {
return data
Expand Down Expand Up @@ -209,16 +224,39 @@ class ReportingDashboard extends React.Component<Props, State> {
});
};

handleDateRangeUpdated = (range: any) => {
const [fromDate, toDate] = range;

this.setState({
fromDate: dayjs(fromDate),
toDate: dayjs(toDate),
});
};

render() {
const {fromDate, toDate} = this.state;

return (
<Box p={4}>
<Box mb={4}>
<Title level={3}>Reporting</Title>
<Flex
mb={4}
sx={{justifyContent: 'space-between', alignItems: 'flex-end'}}
>
<Box>
<Title level={3}>Reporting</Title>

<Paragraph>
Analytics and statistics around user engagement.
</Paragraph>
</Box>
<Paragraph>
Analytics and statistics around user engagement.
</Paragraph>
</Box>

<Box mb={3}>
<RangePicker
value={[fromDate, toDate]}
onChange={this.handleDateRangeUpdated}
/>
</Box>
</Flex>

<Box mb={4}>
<Alert
Expand Down
6 changes: 6 additions & 0 deletions lib/chat_api/chat/message.ex
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,10 @@ defmodule ChatApi.Messages.Message do
])
|> validate_required([:body, :account_id, :conversation_id])
end

def test_changeset(message, attrs) do
message
|> cast(attrs, [:inserted_at])
|> changeset(attrs)
end
end
6 changes: 6 additions & 0 deletions lib/chat_api/conversations.ex
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,12 @@ defmodule ChatApi.Conversations do
|> Repo.insert()
end

def create_test_conversation(attrs \\ %{}) do
%Conversation{}
|> Conversation.test_changeset(attrs)
|> Repo.insert()
end

@doc """
Updates a conversation.
Expand Down
6 changes: 6 additions & 0 deletions lib/chat_api/conversations/conversation.ex
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,10 @@ defmodule ChatApi.Conversations.Conversation do
|> cast(attrs, [:status, :priority, :read, :assignee_id, :account_id, :customer_id])
|> validate_required([:status, :account_id])
end

def test_changeset(conversation, attrs) do
conversation
|> cast(attrs, [:inserted_at])
|> changeset(attrs)
end
end
6 changes: 6 additions & 0 deletions lib/chat_api/messages.ex
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@ defmodule ChatApi.Messages do
|> Repo.insert()
end

def create_test_message(attrs \\ %{}) do
%Message{}
|> Message.test_changeset(attrs)
|> Repo.insert()
end

def create_and_fetch!(attrs \\ %{}) do
case create_message(attrs) do
{:ok, message} -> get_message!(message.id)
Expand Down
14 changes: 10 additions & 4 deletions lib/chat_api/reporting.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,18 @@ defmodule ChatApi.Reporting do
import Ecto.Query, warn: false
alias ChatApi.{Repo, Conversations.Conversation, Messages.Message}

def messages_by_date() do
Message |> count_grouped_by_date() |> Repo.all()
def messages_by_date(account_id) do
Message
|> where(account_id: ^account_id)
|> count_grouped_by_date()
|> Repo.all()
end

def conversations_by_date() do
Conversation |> count_grouped_by_date() |> Repo.all()
def conversations_by_date(account_id) do
Conversation
|> where(account_id: ^account_id)
|> count_grouped_by_date()
|> Repo.all()
end

def count_grouped_by_date(query, field \\ :inserted_at) do
Expand Down
13 changes: 10 additions & 3 deletions lib/chat_api_web/controllers/reporting_controller.ex
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
defmodule ChatApiWeb.ReportingController do
use ChatApiWeb, :controller

require Logger

alias ChatApi.Reporting

action_fallback ChatApiWeb.FallbackController

@spec index(Plug.Conn.t(), map()) :: Plug.Conn.t()
def index(conn, _params) do
def index(%{assigns: %{current_user: %{account_id: account_id}}} = conn, %{
"from_date" => from_date,
"to_date" => to_date
}) do
Logger.info("Fetching reporting from #{from_date} to #{to_date}")

json(conn, %{
data: %{
messages_by_date: Reporting.messages_by_date(),
conversations_by_date: Reporting.conversations_by_date()
messages_by_date: Reporting.messages_by_date(account_id),
conversations_by_date: Reporting.conversations_by_date(account_id)
}
})
end
Expand Down
21 changes: 15 additions & 6 deletions test/chat_api/reporting_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,36 @@ defmodule ChatApi.ReportingTest do
{:ok, account: account, customer: customer}
end

test "messages_by_date/0 retrieves the number of messages created per day", %{
test "messages_by_date/1 retrieves the number of messages created per day", %{
account: account,
customer: customer
} do
count = 10
inserted_at = ~N[2020-09-01 12:00:00]
conversation = conversation_fixture(account, customer)

for _i <- 1..count, do: message_fixture(account, conversation)
for _i <- 1..count do
message_fixture(account, conversation, %{
inserted_at: inserted_at
})
end

assert [%{count: ^count, date: _date}] = Reporting.messages_by_date()
assert [%{count: ^count, date: ~D[2020-09-01]}] = Reporting.messages_by_date(account.id)
end

test "conversations_by_date/0 retrieves the number of conversations created per day", %{
test "conversations_by_date/1 retrieves the number of conversations created per day", %{
account: account,
customer: customer
} do
count = 5
inserted_at = ~N[2020-09-01 12:00:00]

for _i <- 1..count, do: conversation_fixture(account, customer)
for _i <- 1..count do
conversation_fixture(account, customer, %{inserted_at: inserted_at})
end

assert [%{count: ^count, date: _date}] = Reporting.conversations_by_date()
assert [%{count: ^count, date: ~D[2020-09-01]}] =
Reporting.conversations_by_date(account.id)
end
end
end
4 changes: 2 additions & 2 deletions test/support/test_fixture_helpers.ex
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ defmodule ChatApi.TestFixtureHelpers do
customer_id: customer.id
}
|> Enum.into(attrs)
|> Conversations.create_conversation()
|> Conversations.create_test_conversation()

conversation |> Repo.preload([:customer, messages: [user: :profile]])
end
Expand All @@ -92,7 +92,7 @@ defmodule ChatApi.TestFixtureHelpers do
conversation_id: conversation.id,
account_id: account.id
})
|> Messages.create_message()
|> Messages.create_test_message()

Messages.get_message!(message.id)
|> Repo.preload([:conversation, :customer, [user: :profile]])
Expand Down

0 comments on commit cb6f070

Please sign in to comment.