1 import { useMemo, useRef, useState } from 'react';
3 import { endOfDay } from 'date-fns';
4 import { c } from 'ttag';
6 import { useMembers } from '@proton/account/members/hooks';
7 import { Button } from '@proton/atoms';
8 import AddressesInput, { AddressesInputItem } from '@proton/components/components/addressesInput/AddressesInput';
9 import DateInput from '@proton/components/components/input/DateInput';
10 import Loader from '@proton/components/components/loader/Loader';
11 import Pagination from '@proton/components/components/pagination/Pagination';
12 import usePaginationAsync from '@proton/components/components/pagination/usePaginationAsync';
13 import AddressesAutocompleteTwo from '@proton/components/components/v2/addressesAutocomplete/AddressesAutocomplete';
14 import InputField from '@proton/components/components/v2/field/InputField';
15 import SettingsSectionExtraWide from '@proton/components/containers/account/SettingsSectionExtraWide';
16 import useApi from '@proton/components/hooks/useApi';
17 import useNotifications from '@proton/components/hooks/useNotifications';
18 import { validateEmailAddress } from '@proton/shared/lib/helpers/email';
19 import type { Recipient } from '@proton/shared/lib/interfaces';
20 import { type Organization } from '@proton/shared/lib/interfaces';
21 import clsx from '@proton/utils/clsx';
23 import { getLocalTimeStringFromDate } from '../b2bDashboard/Pass/helpers';
24 import B2BAuthLogsTable from '../logs/B2BAuthLogsTable';
25 import { convertEnhancedMembersToContactEmails } from './groups/NewGroupMemberInput';
26 import useAuthLogsDateFilter from './useAuthLogsFilter';
27 import useOrgAuthLogs from './useOrgAuthLogs';
30 organization?: Organization;
32 export interface AuthLogsQueryParams {
38 const AuthenticationLogs = ({ organization }: Props) => {
40 const [members] = useMembers();
41 const { createNotification } = useNotifications();
42 const { page, onNext, onPrevious, onSelect, reset } = usePaginationAsync(1);
43 const [query, setQuery] = useState<AuthLogsQueryParams>({ Emails: [] });
44 const { authLogs, total, loading, error } = useOrgAuthLogs(api, query, page);
45 const { filter, handleStartDateChange, handleEndDateChange } = useAuthLogsDateFilter();
46 const [recipients, setRecipient] = useState<Recipient[]>([]);
47 const addressesAutocompleteRef = useRef<HTMLInputElement>(null);
49 const items = useMemo(() => {
50 return recipients.map((recipient) => {
53 key={recipient.Address}
54 label={recipient.Address}
55 onClick={(event) => event.stopPropagation()}
57 setRecipient(recipients.filter((rec) => rec.Address !== recipient.Address));
64 const today = new Date();
66 if (!organization || !members) {
70 const handleAddEmail = (newRecipients: Recipient[]) => {
71 setRecipient([...recipients, ...newRecipients]);
74 const handleSearchSubmit = () => {
75 if (!recipients.length) {
76 createNotification({ text: c('error').t`Enter an email address` });
80 const Emails = recipients.map((recipient) => recipient.Address);
81 const StartTime = filter.start ? getLocalTimeStringFromDate(filter.start) : undefined;
82 const EndTime = filter.end ? getLocalTimeStringFromDate(endOfDay(filter.end)) : undefined;
83 setQuery({ Emails, StartTime, EndTime });
87 <SettingsSectionExtraWide>
88 <div className="flex flex-column md:flex-row gap-0 md:gap-4 flex-nowrap items-start justify-space-between *:min-size-auto my-6">
91 ref={addressesAutocompleteRef}
92 rootClassName="flex-1"
93 inputContainerClassName=""
95 <AddressesAutocompleteTwo
96 id="auth-log-autocomplete"
97 anchorRef={addressesAutocompleteRef}
98 recipients={recipients}
99 onAddRecipients={handleAddEmail}
100 contactEmails={members && convertEnhancedMembersToContactEmails(members)}
101 inputClassName={clsx([
102 !recipients.length && 'my-0.5',
103 !!recipients.length && 'p-0 rounded-none',
105 validate={(email: string) => {
106 if (!validateEmailAddress(email)) {
107 return c('Input Error').t`Not a valid email address`;
111 placeholder={recipients.length ? '' : c('Label').t`Search by email`}
116 className={clsx(['multi-select-container', !!recipients.length && 'px-2 py-0.5'])}
118 <div className="flex-1 flex flex-column gap-2 *:min-size-auto">
119 <div className="flex flex-1 flex-row flex-nowrap gap-2">
122 placeholder={c('Placeholder').t`Start date`}
124 onChange={handleStartDateChange}
130 placeholder={c('Placeholder').t`End date`}
132 onChange={handleEndDateChange}
136 <Button color="norm" onClick={handleSearchSubmit} disabled={loading}>
137 {c('Action').t`Search`}
140 <div className="self-end">
147 onPrevious={onPrevious}
152 <B2BAuthLogsTable logs={authLogs} loading={loading} error={error} />
153 </SettingsSectionExtraWide>
157 export default AuthenticationLogs;