mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-13 18:21:22 +00:00
fix: UI/UX issues in new banking module (#54824)
* fix: enforce user permissions on bank account get_list * feat: auto-select last used bank account * fix: skeleton loaders in bank balance * fix: show empty state for no bank transactions * chore: add Stripe and PayPal logos * fix: alignment of header text in list-view * fix: wrap words in transaction description * fix: change file-dropzone color on hover
This commit is contained in:
@@ -53,7 +53,7 @@ const OpeningBalance = () => {
|
|||||||
|
|
||||||
return <StatContainer className="min-w-48">
|
return <StatContainer className="min-w-48">
|
||||||
<StatLabel>{_("Opening Balance")}</StatLabel>
|
<StatLabel>{_("Opening Balance")}</StatLabel>
|
||||||
{isLoading ? <Skeleton className="w-[150px] h-9" /> : <StatValue className="font-numeric">{formatCurrency(flt(data?.message, 2), bankAccount?.account_currency ?? getCompanyCurrency(bankAccount?.company ?? ''))}</StatValue>}
|
{isLoading ? <Skeleton className="w-[150px] h-5 rounded-sm" /> : <StatValue className="font-numeric">{formatCurrency(flt(data?.message, 2), bankAccount?.account_currency ?? getCompanyCurrency(bankAccount?.company ?? ''))}</StatValue>}
|
||||||
</StatContainer>
|
</StatContainer>
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +86,7 @@ const ClosingBalance = () => {
|
|||||||
</HoverCard>
|
</HoverCard>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
{isLoading ? <Skeleton className="w-[150px] h-9" /> : <StatValue className="font-numeric">{formatCurrency(flt(data?.message, 2), bankAccount?.account_currency ?? getCompanyCurrency(bankAccount?.company ?? ''))}</StatValue>}
|
{isLoading ? <Skeleton className="w-[150px] h-5 rounded-sm" /> : <StatValue className="font-numeric">{formatCurrency(flt(data?.message, 2), bankAccount?.account_currency ?? getCompanyCurrency(bankAccount?.company ?? ''))}</StatValue>}
|
||||||
</StatContainer>
|
</StatContainer>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -104,7 +104,7 @@ const Difference = () => {
|
|||||||
|
|
||||||
return <StatContainer className="w-fit text-end sm:min-w-56">
|
return <StatContainer className="w-fit text-end sm:min-w-56">
|
||||||
<StatLabel className="text-end">{_("Difference")}</StatLabel>
|
<StatLabel className="text-end">{_("Difference")}</StatLabel>
|
||||||
{isLoading ? <Skeleton className="w-[150px] h-9" /> : <StatValue className={isError ? 'text-ink-red-3 font-numeric' : 'font-numeric'}>
|
{isLoading ? <Skeleton className="w-[150px] h-5 self-end rounded-sm" /> : <StatValue className={isError ? 'text-ink-red-3 font-numeric' : 'font-numeric'}>
|
||||||
{formatCurrency(difference,
|
{formatCurrency(difference,
|
||||||
bankAccount?.account_currency ?? getCompanyCurrency(bankAccount?.company ?? ''))
|
bankAccount?.account_currency ?? getCompanyCurrency(bankAccount?.company ?? ''))
|
||||||
}</StatValue>}
|
}</StatValue>}
|
||||||
@@ -175,7 +175,7 @@ const ClosingBalanceAsPerStatement = () => {
|
|||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
<div className="flex items-center gap-4 underline cursor-pointer underline-offset-6" role="button">
|
<div className="flex items-center gap-4 underline cursor-pointer underline-offset-6" role="button">
|
||||||
{isLoading ? <Skeleton className="w-[150px] h-9" /> : <StatValue className="font-numeric">{formatCurrency(flt(data?.message?.balance, 2), bankAccount?.account_currency ?? getCompanyCurrency(bankAccount?.company ?? ''))}</StatValue>}
|
{isLoading ? <Skeleton className="w-[150px] h-5 rounded-sm" /> : <StatValue className="font-numeric">{formatCurrency(flt(data?.message?.balance, 2), bankAccount?.account_currency ?? getCompanyCurrency(bankAccount?.company ?? ''))}</StatValue>}
|
||||||
<Edit className="w-4 h-4" />
|
<Edit className="w-4 h-4" />
|
||||||
</div>
|
</div>
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useAtom, useSetAtom } from "jotai"
|
import { useAtom } from "jotai"
|
||||||
import { SelectedBank, selectedBankAccountAtom } from "./bankRecAtoms"
|
import { SelectedBank, selectedBankAccountAtom } from "./bankRecAtoms"
|
||||||
import { useCallback } from "react"
|
import { useCallback } from "react"
|
||||||
import { useGetBankAccounts, useGetUnreconciledTransactions } from "./utils"
|
import { useGetBankAccounts, useGetUnreconciledTransactions } from "./utils"
|
||||||
@@ -16,9 +16,16 @@ import { useCurrentCompany } from "@/hooks/useCurrentCompany"
|
|||||||
|
|
||||||
const BankPicker = ({ className }: { className?: string }) => {
|
const BankPicker = ({ className }: { className?: string }) => {
|
||||||
|
|
||||||
const setSelectedBank = useSetAtom(selectedBankAccountAtom)
|
const [selectedBank, setSelectedBank] = useAtom(selectedBankAccountAtom)
|
||||||
|
|
||||||
const onLoadingSuccess = useCallback((data?: SelectedBank[]) => {
|
const onLoadingSuccess = useCallback((data?: SelectedBank[]) => {
|
||||||
|
// If the bank is already selected, then don't set it again
|
||||||
|
if (selectedBank) {
|
||||||
|
// Check if selected bank is in the data
|
||||||
|
if (data?.some((bank: SelectedBank) => bank.name === selectedBank.name)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!data) return
|
if (!data) return
|
||||||
if (data.length === 1) {
|
if (data.length === 1) {
|
||||||
setSelectedBank(data[0])
|
setSelectedBank(data[0])
|
||||||
@@ -26,9 +33,12 @@ const BankPicker = ({ className }: { className?: string }) => {
|
|||||||
const defaultBank = data.find((bank: SelectedBank) => bank.is_default)
|
const defaultBank = data.find((bank: SelectedBank) => bank.is_default)
|
||||||
if (defaultBank) {
|
if (defaultBank) {
|
||||||
setSelectedBank(defaultBank)
|
setSelectedBank(defaultBank)
|
||||||
|
} else {
|
||||||
|
// Select the first available bank account
|
||||||
|
setSelectedBank(data[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [setSelectedBank])
|
}, [setSelectedBank, selectedBank])
|
||||||
|
|
||||||
const selectedCompany = useCurrentCompany()
|
const selectedCompany = useCurrentCompany()
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import { useDebounceValue } from "usehooks-ts"
|
|||||||
import type { ColumnDef } from "@tanstack/react-table"
|
import type { ColumnDef } from "@tanstack/react-table"
|
||||||
import { useCallback, useMemo, useState } from "react"
|
import { useCallback, useMemo, useState } from "react"
|
||||||
import { Link } from "react-router"
|
import { Link } from "react-router"
|
||||||
import { Empty, EmptyTitle, EmptyHeader, EmptyMedia, EmptyDescription } from "@/components/ui/empty"
|
import { Empty, EmptyTitle, EmptyHeader, EmptyMedia, EmptyDescription, EmptyContent } from "@/components/ui/empty"
|
||||||
import { InputGroup, InputGroupAddon } from "@/components/ui/input-group"
|
import { InputGroup, InputGroupAddon } from "@/components/ui/input-group"
|
||||||
|
|
||||||
const BankTransactions = () => {
|
const BankTransactions = () => {
|
||||||
@@ -262,7 +262,7 @@ const BankTransactionListView = () => {
|
|||||||
|
|
||||||
{error && <ErrorBanner error={error} />}
|
{error && <ErrorBanner error={error} />}
|
||||||
|
|
||||||
{data && data.message.length > 0 && <Filters
|
<Filters
|
||||||
onSearchChange={onSearchChange}
|
onSearchChange={onSearchChange}
|
||||||
search={search}
|
search={search}
|
||||||
results={filteredResults}
|
results={filteredResults}
|
||||||
@@ -272,28 +272,31 @@ const BankTransactionListView = () => {
|
|||||||
typeFilter={typeFilter}
|
typeFilter={typeFilter}
|
||||||
status={status}
|
status={status}
|
||||||
setStatus={setStatus}
|
setStatus={setStatus}
|
||||||
/>}
|
/>
|
||||||
|
|
||||||
{data && data.message.length > 0 ? (
|
|
||||||
<ListView
|
|
||||||
data={filteredResults}
|
|
||||||
columns={transactionColumns}
|
|
||||||
getRowId={(row) => row.name}
|
|
||||||
maxHeight="calc(100vh - 200px)"
|
|
||||||
scrollAreaClassName="min-h-[calc(100vh-200px)]"
|
|
||||||
emptyState={<Empty>
|
|
||||||
<EmptyMedia>
|
|
||||||
<ListIcon />
|
|
||||||
</EmptyMedia>
|
|
||||||
<EmptyHeader>
|
|
||||||
<EmptyTitle>{_("No bank transactions found")}</EmptyTitle>
|
|
||||||
<EmptyDescription>{_("There are no transactions in the system for the selected bank account and dates that match the filters.")}</EmptyDescription>
|
|
||||||
</EmptyHeader>
|
|
||||||
</Empty>}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
|
|
||||||
|
|
||||||
|
<ListView
|
||||||
|
data={filteredResults}
|
||||||
|
columns={transactionColumns}
|
||||||
|
getRowId={(row) => row.name}
|
||||||
|
maxHeight="calc(100vh - 200px)"
|
||||||
|
scrollAreaClassName="min-h-[calc(100vh-200px)]"
|
||||||
|
emptyState={<Empty>
|
||||||
|
<EmptyMedia>
|
||||||
|
<ListIcon />
|
||||||
|
</EmptyMedia>
|
||||||
|
<EmptyHeader>
|
||||||
|
<EmptyTitle>{_("No bank transactions found")}</EmptyTitle>
|
||||||
|
<EmptyDescription>{_("There are no transactions in the system for the selected bank account and dates that match the filters.")}</EmptyDescription>
|
||||||
|
</EmptyHeader>
|
||||||
|
{data && data.message.length === 0 ? <EmptyContent>
|
||||||
|
<Button type='button' asChild variant='outline'>
|
||||||
|
<Link to="/statement-importer">
|
||||||
|
{_("Import Bank Statement")}
|
||||||
|
</Link>
|
||||||
|
</Button>
|
||||||
|
</EmptyContent> : null}
|
||||||
|
</Empty>}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -298,7 +298,7 @@ const UnreconciledTransactionItem = ({ transaction }: { transaction: Unreconcile
|
|||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
onClick={handleSelectTransaction}>
|
onClick={handleSelectTransaction}>
|
||||||
<div className="flex justify-between items-start w-full">
|
<div className="flex justify-between items-start w-full">
|
||||||
<div className="space-y-1">
|
<div className="space-y-1 overflow-hidden whitespace-pre-wrap">
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
<span className="font-medium text-sm">{formatDate(transaction.date)}</span>
|
<span className="font-medium text-sm">{formatDate(transaction.date)}</span>
|
||||||
{transaction.transaction_type &&
|
{transaction.transaction_type &&
|
||||||
@@ -314,7 +314,7 @@ const UnreconciledTransactionItem = ({ transaction }: { transaction: Unreconcile
|
|||||||
title={_("Matched by rule")}>
|
title={_("Matched by rule")}>
|
||||||
<ZapIcon className="w-4 h-4" /> {transaction.matched_transaction_rule}</Badge>}
|
<ZapIcon className="w-4 h-4" /> {transaction.matched_transaction_rule}</Badge>}
|
||||||
</div>
|
</div>
|
||||||
<span className="text-sm">{transaction.description}</span>
|
<span className="text-sm wrap-anywhere" title={transaction.description}>{transaction.description}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="gap-1 flex flex-col items-end min-w-36 h-full text-end">
|
<div className="gap-1 flex flex-col items-end min-w-36 h-full text-end">
|
||||||
{isWithdrawal ? <ArrowUpRight className="size-5 text-ink-red-3" /> : <ArrowDownRight className="size-5 text-ink-green-3" />}
|
{isWithdrawal ? <ArrowUpRight className="size-5 text-ink-red-3" /> : <ArrowDownRight className="size-5 text-ink-green-3" />}
|
||||||
|
|||||||
@@ -15,7 +15,9 @@ export interface SelectedBank extends Pick<BankAccount, 'name' | 'bank' | 'is_cr
|
|||||||
logoClassName?: string,
|
logoClassName?: string,
|
||||||
account_currency?: string
|
account_currency?: string
|
||||||
}
|
}
|
||||||
export const selectedBankAccountAtom = atom<SelectedBank | null>(null)
|
export const selectedBankAccountAtom = atomWithStorage<SelectedBank | null>('bank-rec-selected-bank', null, undefined, {
|
||||||
|
getOnInit: true
|
||||||
|
})
|
||||||
|
|
||||||
export const bankRecDateAtom = atomWithStorage<{ fromDate: string, toDate: string }>("bank-rec-date", {
|
export const bankRecDateAtom = atomWithStorage<{ fromDate: string, toDate: string }>("bank-rec-date", {
|
||||||
fromDate: getDatesForTimePeriod('This Month').fromDate,
|
fromDate: getDatesForTimePeriod('This Month').fromDate,
|
||||||
|
|||||||
@@ -393,5 +393,18 @@ export const BANK_LOGOS: { keywords: string[], logo: string, locale?: string[],
|
|||||||
logo: "Prime_Bank.png",
|
logo: "Prime_Bank.png",
|
||||||
locale: ['Kenya'],
|
locale: ['Kenya'],
|
||||||
logoClassName: 'max-w-28'
|
logoClassName: 'max-w-28'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
keywords: ["Stripe"],
|
||||||
|
logo: "Stripe.svg",
|
||||||
|
locale: ['Global'],
|
||||||
|
logoClassName: 'h-6',
|
||||||
|
darkModeInvert: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
keywords: ["PayPal"],
|
||||||
|
logo: "PayPal.png",
|
||||||
|
locale: ['Global'],
|
||||||
|
logoClassName: 'h-6',
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -31,7 +31,7 @@ export const FileDropzone = ({ files, setFiles, accept, multiple = true, onDrop,
|
|||||||
}, [setFiles, onDrop, multiple, onUpdate])
|
}, [setFiles, onDrop, multiple, onUpdate])
|
||||||
const { getRootProps, getInputProps } = useDropzone({ onDrop: onFileDrop, accept, multiple })
|
const { getRootProps, getInputProps } = useDropzone({ onDrop: onFileDrop, accept, multiple })
|
||||||
return (
|
return (
|
||||||
<div {...getRootProps()} className={cn('border border-outline-gray-2 border-dashed p-4 rounded bg-surface-gray-1 focus-within:bg-surface-gray-2 focus-within:border-outline-gray-4 focus-within:outline-none', className)}>
|
<div {...getRootProps()} className={cn('border border-outline-gray-2 border-dashed p-4 rounded bg-surface-gray-1 focus-within:bg-surface-gray-2 hover:bg-surface-gray-2 hover:border-outline-gray-3 focus-within:border-outline-gray-3 focus-within:outline-none', className)}>
|
||||||
<input {...getInputProps()} />
|
<input {...getInputProps()} />
|
||||||
{files.length === 0 ? <p className='text-sm text-ink-gray-5 text-center h-8 flex items-center justify-center'>{multiple ? _("Drop some files here, or click to select files") : _("Drop a file here, or click to select a file")}</p> : null}
|
{files.length === 0 ? <p className='text-sm text-ink-gray-5 text-center h-8 flex items-center justify-center'>{multiple ? _("Drop some files here, or click to select files") : _("Drop a file here, or click to select a file")}</p> : null}
|
||||||
<div className='flex flex-col gap-4'>
|
<div className='flex flex-col gap-4'>
|
||||||
|
|||||||
@@ -382,7 +382,7 @@ function ListViewInner<TData>({
|
|||||||
<div
|
<div
|
||||||
key={header.id}
|
key={header.id}
|
||||||
className={cn(
|
className={cn(
|
||||||
"text-ink-gray-5 group relative flex min-w-0 items-center px-1 text-sm",
|
"text-ink-gray-5 group relative flex min-w-0 items-center px-0 text-sm",
|
||||||
alignClass(meta),
|
alignClass(meta),
|
||||||
)}
|
)}
|
||||||
role="columnheader"
|
role="columnheader"
|
||||||
@@ -397,7 +397,7 @@ function ListViewInner<TData>({
|
|||||||
<span
|
<span
|
||||||
aria-hidden
|
aria-hidden
|
||||||
className={cn(
|
className={cn(
|
||||||
"pointer-events-none absolute ltr:right-0 rtl:left-0 z-1 w-0.5 bg-gray-400",
|
"pointer-events-none absolute ltr:-right-2 rtl:-left-2 z-1 w-0.5 bg-gray-400",
|
||||||
"opacity-0 transition-[opacity,background-color] ease-in-out duration-150",
|
"opacity-0 transition-[opacity,background-color] ease-in-out duration-150",
|
||||||
"group-hover:opacity-100 group-hover:bg-gray-400",
|
"group-hover:opacity-100 group-hover:bg-gray-400",
|
||||||
header.column.getIsResizing() && "bg-outline-gray-6 opacity-100",
|
header.column.getIsResizing() && "bg-outline-gray-6 opacity-100",
|
||||||
@@ -421,7 +421,7 @@ function ListViewInner<TData>({
|
|||||||
header.getResizeHandler()(e)
|
header.getResizeHandler()(e)
|
||||||
}}
|
}}
|
||||||
onTouchStart={header.getResizeHandler()}
|
onTouchStart={header.getResizeHandler()}
|
||||||
className="absolute top-0 ltr:right-0 rtl:left-0 z-10 h-full w-2 max-w-[12px] cursor-col-resize touch-none select-none bg-transparent"
|
className="absolute top-0 ltr:-right-2 rtl:-left-2 z-10 h-full w-2 max-w-[12px] cursor-col-resize touch-none select-none bg-transparent"
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ from frappe.contacts.address_and_contact import (
|
|||||||
)
|
)
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.utils import comma_and, get_link_to_form
|
from frappe.utils import comma_and, get_link_to_form
|
||||||
from pypika import Order
|
|
||||||
|
|
||||||
|
|
||||||
class BankAccount(Document):
|
class BankAccount(Document):
|
||||||
@@ -139,38 +138,35 @@ def get_list(company: str, show_disabled: bool = False):
|
|||||||
@return: A list of bank accounts
|
@return: A list of bank accounts
|
||||||
"""
|
"""
|
||||||
|
|
||||||
frappe.has_permission("Bank Account", ptype="read", throw=True)
|
filters = {"is_company_account": 1, "company": company}
|
||||||
|
if not show_disabled:
|
||||||
|
filters["disabled"] = 0
|
||||||
|
|
||||||
bank_account = frappe.qb.DocType("Bank Account")
|
bank_accounts = frappe.get_list(
|
||||||
account = frappe.qb.DocType("Account")
|
"Bank Account",
|
||||||
|
filters=filters,
|
||||||
query = (
|
order_by="is_default desc",
|
||||||
frappe.qb.from_(bank_account)
|
fields=[
|
||||||
.join(account)
|
"name",
|
||||||
.on(bank_account.account == account.name)
|
"account",
|
||||||
.select(
|
"company",
|
||||||
bank_account.name,
|
"account_name",
|
||||||
account.account_currency,
|
"is_default",
|
||||||
bank_account.account,
|
"bank",
|
||||||
bank_account.company,
|
"account_type",
|
||||||
bank_account.account_name,
|
"account_subtype",
|
||||||
bank_account.is_default,
|
"bank_account_no",
|
||||||
bank_account.bank,
|
"last_integration_date",
|
||||||
bank_account.account_type,
|
"is_credit_card",
|
||||||
bank_account.account_subtype,
|
],
|
||||||
bank_account.bank_account_no,
|
|
||||||
bank_account.last_integration_date,
|
|
||||||
bank_account.is_credit_card,
|
|
||||||
)
|
|
||||||
.where(bank_account.is_company_account == 1)
|
|
||||||
.where(bank_account.company == company)
|
|
||||||
.orderby(bank_account.is_default, order=Order.desc)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if not show_disabled:
|
for bank_account in bank_accounts:
|
||||||
query = query.where(bank_account.disabled == 0)
|
bank_account.account_currency = frappe.get_cached_value(
|
||||||
|
"Account", bank_account.account, "account_currency"
|
||||||
|
)
|
||||||
|
|
||||||
return query.run(as_dict=True)
|
return bank_accounts
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist(methods=["GET"])
|
@frappe.whitelist(methods=["GET"])
|
||||||
|
|||||||
BIN
erpnext/public/images/bank-logos/PayPal.png
Normal file
BIN
erpnext/public/images/bank-logos/PayPal.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
9
erpnext/public/images/bank-logos/Stripe.svg
Normal file
9
erpnext/public/images/bank-logos/Stripe.svg
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<svg width="360" height="150" viewBox="0 0 360 150" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M360 77.4001C360 51.8001 347.6 31.6001 323.9 31.6001C300.1 31.6001 285.7 51.8001 285.7 77.2001C285.7 107.3 302.7 122.5 327.1 122.5C339 122.5 348 119.8 354.8 116V96.0001C348 99.4001 340.2 101.5 330.3 101.5C320.6 101.5 312 98.1001 310.9 86.3001H359.8C359.8 85.0001 360 79.8001 360 77.4001ZM310.6 67.9001C310.6 56.6001 317.5 51.9001 323.8 51.9001C329.9 51.9001 336.4 56.6001 336.4 67.9001H310.6Z" fill="#533AFD"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M247.1 31.6001C237.3 31.6001 231 36.2001 227.5 39.4001L226.2 33.2001H204.2V149.8L229.2 144.5L229.3 116.2C232.9 118.8 238.2 122.5 247 122.5C264.9 122.5 281.2 108.1 281.2 76.4001C281.1 47.4001 264.6 31.6001 247.1 31.6001ZM241.1 100.5C235.2 100.5 231.7 98.4001 229.3 95.8001L229.2 58.7001C231.8 55.8001 235.4 53.8001 241.1 53.8001C250.2 53.8001 256.5 64.0001 256.5 77.1001C256.5 90.5001 250.3 100.5 241.1 100.5Z" fill="#533AFD"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M169.8 25.7L194.9 20.3V0L169.8 5.3V25.7Z" fill="#533AFD"/>
|
||||||
|
<path d="M194.9 33.3H169.8V120.8H194.9V33.3Z" fill="#533AFD"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M142.9 40.7L141.3 33.3H119.7V120.8H144.7V61.5C150.6 53.8 160.6 55.2 163.7 56.3V33.3C160.5 32.1 148.8 29.9 142.9 40.7Z" fill="#533AFD"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M92.8999 11.6001L68.4999 16.8001L68.3999 96.9001C68.3999 111.7 79.4999 122.6 94.2999 122.6C102.5 122.6 108.5 121.1 111.8 119.3V99.0001C108.6 100.3 92.7999 104.9 92.7999 90.1001V54.6001H111.8V33.3001H92.7999L92.8999 11.6001Z" fill="#533AFD"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M25.3 58.7001C25.3 54.8001 28.5 53.3001 33.8 53.3001C41.4 53.3001 51 55.6001 58.6 59.7001V36.2001C50.3 32.9001 42.1 31.6001 33.8 31.6001C13.5 31.6001 0 42.2001 0 59.9001C0 87.5001 38 83.1001 38 95.0001C38 99.6001 34 101.1 28.4 101.1C20.1 101.1 9.5 97.7001 1.1 93.1001V116.9C10.4 120.9 19.8 122.6 28.4 122.6C49.2 122.6 63.5 112.3 63.5 94.4001C63.4 64.6001 25.3 69.9001 25.3 58.7001Z" fill="#533AFD"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.1 KiB |
Reference in New Issue
Block a user