Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inviting client members while creating clients #1445

Merged
merged 24 commits into from
Sep 19, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
a99c45c
add flow for inviting client members
Aug 8, 2023
8d732a1
change-invite-flow
Aug 9, 2023
ffda440
show invitations on clientinfo
Aug 10, 2023
2728b47
show accept invite on top
Aug 10, 2023
ef7d138
remove email from client form
Aug 10, 2023
7866ce6
send client members email to send invoice
Aug 21, 2023
3e4f12f
send client member emails for payment reminders
Aug 21, 2023
323fcba
Merge branch 'develop' into client-memeber-invite-flow
prasanthchaduvula Sep 1, 2023
93a00ce
Fixed the create client
prasanthchaduvula Sep 1, 2023
2e69cff
updated client members add icon
prasanthchaduvula Sep 1, 2023
05859eb
Fixed send invoice modals with client emails along with remove option
prasanthchaduvula Sep 1, 2023
2fe1a3f
Merge branch 'develop' into client-memeber-invite-flow
prasanthchaduvula Sep 1, 2023
52f9702
Added multiple client emails on client payment reminder
prasanthchaduvula Sep 1, 2023
198607c
Fixed the invoice client memebers
prasanthchaduvula Sep 4, 2023
5b1f212
Auto refresh client details after adding client contact
prasanthchaduvula Sep 5, 2023
00b704e
Merge branch 'develop' into client-memeber-invite-flow
prasanthchaduvula Sep 8, 2023
b153178
Fixed ailing test by removing the client email
prasanthchaduvula Sep 8, 2023
41bade1
Merge branch 'develop' into client-memeber-invite-flow
prasanthchaduvula Sep 14, 2023
8d837d9
Update create_spec.rb
prasanthchaduvula Sep 14, 2023
992f1a5
fixed the failing test cases
prasanthchaduvula Sep 14, 2023
dca7e96
Merge branch 'develop' into client-memeber-invite-flow
prasanthchaduvula Sep 14, 2023
74c2872
Resolved conflicts
prasanthchaduvula Sep 18, 2023
893d07f
removed the email from client list
prasanthchaduvula Sep 18, 2023
8ee741c
updated add contacts modal
prasanthchaduvula Sep 18, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 17 additions & 7 deletions app/controllers/internal_api/v1/clients_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,21 @@ def index

def create
authorize Client
ActiveRecord::Base.transaction do
client = Client.create!(client_params)
user = User.find_by!(email: params[:client][:email])
client_member = current_company.client_members.create!(client:, user:)
render :create, locals: { client:, address: client.current_address }
end
client = Client.create!(client_params)
render :create, locals: { client:, address: client.current_address }
end

def add_client_contact
authorize client
invitation_service = Invitations::ClientInvitationService.new(
params,
current_company,
current_user,
client
)

invitations = invitation_service.process
render json: { notice: "Invitation sent successfully." }, status: :ok
end

def show
Expand All @@ -25,7 +34,8 @@ def show
project_details: client.project_details(params[:time_frame]),
total_minutes: client.total_hours_logged(params[:time_frame]),
overdue_outstanding_amount: client.client_overdue_and_outstanding_calculation,
invoices: client.invoices
invoices: client.invoices,
client_members: client.invitations
prasanthchaduvula marked this conversation as resolved.
Show resolved Hide resolved
},
status: :ok
end
Expand Down
4 changes: 4 additions & 0 deletions app/javascript/src/apis/clients.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ const destroy = async id => axios.delete(`${path}/${id}`);
const sendPaymentReminder = async (id, payload) =>
axios.post(`${path}/${id}/send_payment_reminder`, payload);

const addClientContact = async (id, payload) =>
axios.post(`${path}/${id}/add_client_contact`, payload);

const invoices = async (query = "") =>
axios.get(query ? `${path}/invoices?${query}` : `${path}/invoices`);

Expand All @@ -26,6 +29,7 @@ const clientApi = {
create,
invoices,
sendPaymentReminder,
addClientContact,
};

export default clientApi;
3 changes: 3 additions & 0 deletions app/javascript/src/common/CustomAdvanceInput/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ type CustomAdvanceInputProps = {
inputBoxClassName?: string;
labelClassName?: string;
onClick?: React.MouseEventHandler<any>;
onBlur?: React.FocusEventHandler<any>;
};

const getDefaultInputBoxClassName = focused =>
Expand All @@ -34,6 +35,7 @@ export const CustomAdvanceInput = ({
inputBoxClassName,
labelClassName,
onClick,
onBlur,
}: CustomAdvanceInputProps) => {
const inputRef = useRef(null);
const [focused, setFocused] = useState<boolean>(false);
Expand All @@ -57,6 +59,7 @@ export const CustomAdvanceInput = ({
<div
className={classNames(defaultInputBoxClassName, inputBoxClassName)}
id={id}
onBlur={onBlur}
onClick={onClick}
>
{value}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ export const clientSchema = Yup.object().shape({
name: Yup.string()
.required("Name cannot be blank")
.max(30, "Maximum 30 characters are allowed"),
email: Yup.string().required("Email cannot be blank"),
phone: Yup.string()
.required("Business phone number cannot be blank")
.matches(phoneRegExp, "Please enter a valid business phone number"),
Expand Down
27 changes: 1 addition & 26 deletions app/javascript/src/components/Clients/ClientForm/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ const ClientForm = ({
submitting,
setSubmitting,
fetchDetails,
usersWithClientRole,
}: IClientForm) => {
const [fileUploadError, setFileUploadError] = useState<string>("");
const [countries, setCountries] = useState([]);
Expand Down Expand Up @@ -114,8 +113,7 @@ const ClientForm = ({
onSubmit={handleSubmit}
>
{(props: FormikProps<FormValues>) => {
const { touched, errors, setFieldValue, values, setFieldTouched } =
props;
const { touched, errors, setFieldValue, values } = props;

return (
<Form>
Expand Down Expand Up @@ -153,24 +151,6 @@ const ClientForm = ({
fieldTouched={touched.name}
/>
</div>
<div className="mt-4">
<CustomReactSelect
handleOnChange={email => setFieldValue("email", email.value)}
id="email"
label="Email"
name="email"
value={{ label: values.email, value: values.email }}
options={usersWithClientRole.map(user => ({
value: user.email,
label: user.email,
}))}
onBlur={() => setFieldTouched("email", true)}
/>
<InputErrors
fieldErrors={errors.email?.value}
fieldTouched={touched.email}
/>
</div>
<div className="mt-4">
<div className="field relative">
<div className="flex flex-col">
Expand Down Expand Up @@ -330,15 +310,10 @@ interface IClientForm {
submitting: boolean;
setSubmitting: any;
fetchDetails?: any;
usersWithClientRole: any;
}

interface FormValues {
name: string;
email: {
value: string;
label: string;
};
phone: string;
address1: string;
address2: string;
Expand Down
7 changes: 0 additions & 7 deletions app/javascript/src/components/Clients/ClientForm/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,12 @@ export const formatFormData = (
clientLogoUrl
) => {
formData.append("client[name]", values.name);
formData.append("client[email]", values.email);
formData.append("client[phone]", values.phone);

if (!isNewForm) {
formData.append("client[addresses_attributes[0][id]]", client.address.id);
}

if (!isNewForm && values.email !== client.email) {
formData.append("client[prev_email]", client.email);
}

formData.append(
"client[addresses_attributes[0][address_line_1]]",
values.address1
Expand Down Expand Up @@ -58,7 +53,6 @@ export const formatFormData = (
export const disableBtn = (values, errors, submitting) => {
if (
errors.name ||
errors.email ||
errors.phone ||
errors.address1 ||
errors.country ||
Expand All @@ -72,7 +66,6 @@ export const disableBtn = (values, errors, submitting) => {

if (
values.name &&
values.email &&
values.phone &&
values.address1 &&
values.country &&
Expand Down
44 changes: 42 additions & 2 deletions app/javascript/src/components/Clients/Details/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ import {
ReminderIcon,
} from "miruIcons";
import { useNavigate } from "react-router-dom";
import { MobileMoreOptions, Modal } from "StyledComponents";
import { Badge, MobileMoreOptions, Modal } from "StyledComponents";

import { useUserContext } from "context/UserContext";

import AddContacts from "../Modals/AddContacts";
import DeleteClient from "../Modals/DeleteClient";
import EditClient from "../Modals/EditClient";

Expand All @@ -25,14 +26,24 @@ const Header = ({
setShowProjectModal,
fetchDetails,
setSendPaymentReminder,
contactDetails,
}) => {
const [isHeaderMenuVisible, setIsHeaderMenuVisible] =
useState<boolean>(false);
const [isClientOpen, setIsClientOpen] = useState<boolean>(false);
const [showDeleteDialog, setShowDeleteDialog] = useState<boolean>(false);
const [showEditDialog, setShowEditDialog] = useState<boolean>(false);
const [showMobileModal, setShowMobileModal] = useState<boolean>(false);
const [showContactModal, setShowContactModal] = useState<boolean>(false);

const acceptedEmails = contactDetails.filter(
contact => contact.accepted_at !== null
);

const pendingInvitationEmails = contactDetails.filter(
contact => contact.accepted_at === null
);
const sortedEmails = [...acceptedEmails, ...pendingInvitationEmails];
const navigate = useNavigate();
const menuRef = useRef();
const { isDesktop } = useUserContext();
Expand Down Expand Up @@ -116,6 +127,17 @@ const Header = ({
<span className="ml-3">Payment Reminder</span>
</button>
</li>
<li
onClick={() => {
setShowContactModal(true);
setIsHeaderMenuVisible(false);
}}
>
<button className="menuButton__list-item">
<ReminderIcon id="reminderIcon" size={16} />
<span className="ml-3">Add Contacts</span>
</button>
</li>
<li onClick={handleDelete}>
<button className="menuButton__list-item text-miru-red-400">
<DeleteIcon color="#E04646" size={16} weight="bold" />
Expand All @@ -133,7 +155,18 @@ const Header = ({
<div className="mt-4 text-base">
<p className="font-semibold">Email ID(s)</p>
<p className="mt-1 text-miru-dark-purple-400">
{clientDetails.email}
{sortedEmails.map((contact, index) => (
<p className="mb-2" key={index}>
{contact.accepted_at !== null ? (
contact.recipient_email
) : (
<div className="flex justify-between">
{contact.recipient_email}
<Badge text="Pending Invitation" />
</div>
)}
</p>
))}
</p>
</div>
<div className="mt-4 text-base">
Expand Down Expand Up @@ -169,6 +202,13 @@ const Header = ({
showEditDialog={showEditDialog}
/>
)}
{showContactModal && (
<AddContacts
client={clientDetails}
setShowContactModal={setShowContactModal}
showContactModal={showContactModal}
/>
)}
{showMobileModal && (
<MobileMoreOptions
setVisibilty={setShowMobileModal}
Expand Down
4 changes: 3 additions & 1 deletion app/javascript/src/components/Clients/Details/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@ const ClientDetails = ({ isAdminUser }) => {
const [projectDetails, setProjectDetails] = useState<any>();
const [totalMinutes, setTotalMinutes] = useState(null);
const [clientDetails, setClientDetails] = useState<any>({});
const [contactDetails, setContactDetails] = useState<any>({});
const [editProjectData, setEditProjectData] = useState<any>({});
const [showProjectModal, setShowProjectModal] = useState<boolean>(false);
const [loading, setLoading] = useState(true);
const [overdueOutstandingAmount, setOverdueOutstandingAmount] =
useState<any>(null);
const [clientInvoices, setClientInvoices] = useState<any[]>([]);

const [sendPaymentReminder, setSendPaymentReminder] =
useState<boolean>(false);

Expand Down Expand Up @@ -75,6 +75,7 @@ const ClientDetails = ({ isAdminUser }) => {
try {
const res = await clientApi.show(params.clientId, "?time_frame=week");
const sanitized = unmapClientDetails(res);
setContactDetails(sanitized.contactDetails);
setClientDetails(sanitized.clientDetails);
setProjectDetails(sanitized.projectDetails);
setTotalMinutes(sanitized.totalMinutes);
Expand Down Expand Up @@ -132,6 +133,7 @@ const ClientDetails = ({ isAdminUser }) => {
<>
<Header
clientDetails={clientDetails}
contactDetails={contactDetails}
fetchDetails={fetchProjectList}
setSendPaymentReminder={setSendPaymentReminder}
setShowProjectModal={setShowProjectModal}
Expand Down
Loading
Loading