Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
赵月辉
/
fastgpt-migrated
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Unverified
Commit
a997f277
authored
Dec 19, 2024
by
Archer
Committed by
GitHub
Dec 19, 2024
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
4.8.16 test (#3437)
* perf: auto login code * perf: invoice phone
parent
1f4e5f6d
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
95 additions
and
108 deletions
+95
-108
packages/web/i18n/en/account_bill.json
+1
-0
packages/web/i18n/zh-CN/account_bill.json
+3
-2
packages/web/i18n/zh-Hant/account_bill.json
+1
-0
projects/app/src/pages/account/bill/components/ApplyInvoiceModal.tsx
+64
-61
projects/app/src/pages/account/bill/components/InvoiceHeaderForm.tsx
+2
-4
projects/app/src/pages/login/components/LoginForm/components/FormLayout.tsx
+16
-34
projects/app/src/pages/login/index.tsx
+7
-6
projects/app/src/web/context/useInitApp.ts
+1
-1
No files found.
packages/web/i18n/en/account_bill.json
View file @
a997f277
...
@@ -45,6 +45,7 @@
...
@@ -45,6 +45,7 @@
"support_wallet_bill_tag_invoice"
:
"bill invoice"
,
"support_wallet_bill_tag_invoice"
:
"bill invoice"
,
"support_wallet_invoicing"
:
"Invoicing"
,
"support_wallet_invoicing"
:
"Invoicing"
,
"time"
:
"time"
,
"time"
:
"time"
,
"total_amount"
:
"lump sum"
,
"type"
:
"type"
,
"type"
:
"type"
,
"unit_code"
:
"unified credit code"
,
"unit_code"
:
"unified credit code"
,
"unit_code_void"
:
"Unified credit code format error"
,
"unit_code_void"
:
"Unified credit code format error"
,
...
...
packages/web/i18n/zh-CN/account_bill.json
View file @
a997f277
...
@@ -9,10 +9,10 @@
...
@@ -9,10 +9,10 @@
"company_phone"
:
"公司电话"
,
"company_phone"
:
"公司电话"
,
"completed"
:
"已完成"
,
"completed"
:
"已完成"
,
"confirm"
:
"确认"
,
"confirm"
:
"确认"
,
"default_header"
:
"默认抬头"
,
"detail"
:
"详情"
,
"contact_phone"
:
"联系电话"
,
"contact_phone"
:
"联系电话"
,
"contact_phone_void"
:
"联系电话格式错误"
,
"contact_phone_void"
:
"联系电话格式错误"
,
"default_header"
:
"默认抬头"
,
"detail"
:
"详情"
,
"email_address"
:
"邮箱地址"
,
"email_address"
:
"邮箱地址"
,
"extra_ai_points"
:
"额外 AI 积分"
,
"extra_ai_points"
:
"额外 AI 积分"
,
"extra_dataset_size"
:
"额外知识库容量"
,
"extra_dataset_size"
:
"额外知识库容量"
,
...
@@ -45,6 +45,7 @@
...
@@ -45,6 +45,7 @@
"support_wallet_bill_tag_invoice"
:
"账单发票"
,
"support_wallet_bill_tag_invoice"
:
"账单发票"
,
"support_wallet_invoicing"
:
"开票"
,
"support_wallet_invoicing"
:
"开票"
,
"time"
:
"时间"
,
"time"
:
"时间"
,
"total_amount"
:
"总金额"
,
"type"
:
"类型"
,
"type"
:
"类型"
,
"unit_code"
:
"统一信用代码"
,
"unit_code"
:
"统一信用代码"
,
"unit_code_void"
:
"统一信用代码格式错误"
,
"unit_code_void"
:
"统一信用代码格式错误"
,
...
...
packages/web/i18n/zh-Hant/account_bill.json
View file @
a997f277
...
@@ -45,6 +45,7 @@
...
@@ -45,6 +45,7 @@
"support_wallet_bill_tag_invoice"
:
"帳單發票"
,
"support_wallet_bill_tag_invoice"
:
"帳單發票"
,
"support_wallet_invoicing"
:
"開票"
,
"support_wallet_invoicing"
:
"開票"
,
"time"
:
"時間"
,
"time"
:
"時間"
,
"total_amount"
:
"總金額"
,
"type"
:
"類型"
,
"type"
:
"類型"
,
"unit_code"
:
"統一信用代碼"
,
"unit_code"
:
"統一信用代碼"
,
"unit_code_void"
:
"統一信用代碼格式錯誤"
,
"unit_code_void"
:
"統一信用代碼格式錯誤"
,
...
...
projects/app/src/pages/account/bill/components/ApplyInvoiceModal.tsx
View file @
a997f277
...
@@ -8,6 +8,8 @@ import {
...
@@ -8,6 +8,8 @@ import {
Button
,
Button
,
Checkbox
,
Checkbox
,
Flex
,
Flex
,
ModalBody
,
ModalFooter
,
Table
,
Table
,
TableContainer
,
TableContainer
,
Tbody
,
Tbody
,
...
@@ -118,19 +120,69 @@ const ApplyInvoiceModal = ({ onClose }: { onClose: () => void }) => {
...
@@ -118,19 +120,69 @@ const ApplyInvoiceModal = ({ onClose }: { onClose: () => void }) => {
isOpen=
{
true
}
isOpen=
{
true
}
isCentered
isCentered
iconSrc=
"/imgs/modal/invoice.svg"
iconSrc=
"/imgs/modal/invoice.svg"
minHeight=
{
isOpenSettleModal
?
'46.4rem'
:
'42.25rem'
}
w=
{
'43rem'
}
w=
{
'43rem'
}
onClose=
{
onClose
}
onClose=
{
onClose
}
isLoading=
{
isLoading
}
isLoading=
{
isLoading
}
title=
{
t
(
'account_bill:support_wallet_apply_invoice'
)
}
title=
{
t
(
'account_bill:support_wallet_apply_invoice'
)
}
>
>
{
!
isOpenSettleModal
?
(
{
isOpenSettleModal
?
(
<
Box
px=
{
[
'1.6rem'
,
'3.25rem'
]
}
py=
{
[
'1rem'
,
'2rem'
]
}
>
<>
<
Box
fontWeight=
{
500
}
fontSize=
{
'1rem'
}
pb=
{
'0.75rem'
}
>
<
ModalBody
>
{
t
(
'account_bill:support_wallet_apply_invoice'
)
}
<
Box
w=
{
'100%'
}
fontSize=
{
'0.875rem'
}
>
</
Box
>
<
Flex
w=
{
'100%'
}
justifyContent=
{
'space-between'
}
>
<
Box
h=
{
'27.9rem'
}
overflow=
{
'auto'
}
>
<
Box
>
{
t
(
'account_bill:total_amount'
)
}
</
Box
>
<
TableContainer
>
<
Box
>
{
t
(
'account_bill:yuan'
,
{
amount
:
formatStorePrice2Read
(
totalPrice
)
})
}
</
Box
>
</
Flex
>
<
Box
w=
{
'100%'
}
py=
{
4
}
>
<
Divider
showBorderBottom=
{
false
}
/>
</
Box
>
</
Box
>
<
MyBox
isLoading=
{
isLoadingHeader
}
>
<
Flex
justify=
{
'center'
}
>
<
InvoiceHeaderSingleForm
inputForm=
{
inputForm
}
required
/>
</
Flex
>
</
MyBox
>
<
Flex
align=
{
'center'
}
w=
{
'19.8rem'
}
h=
{
'1.75rem'
}
mt=
{
4
}
px=
{
'0.75rem'
}
py=
{
'0.38rem'
}
bg=
{
'blue.50'
}
borderRadius=
{
'sm'
}
color=
{
'blue.600'
}
>
<
MyIcon
name=
"infoRounded"
w=
{
'14px'
}
h=
{
'14px'
}
/>
<
Box
ml=
{
2
}
fontSize=
{
'0.6875rem'
}
>
{
t
(
'account_bill:invoice_sending_info'
)
}
</
Box
>
</
Flex
>
</
ModalBody
>
<
ModalFooter
>
<
Button
variant=
{
'outline'
}
mr=
{
'0.75rem'
}
px=
"0"
onClick=
{
handleBack
}
>
<
Flex
alignItems=
{
'center'
}
>
<
Box
px=
{
'1.25rem'
}
py=
{
'0.5rem'
}
>
{
t
(
'account_bill:back'
)
}
</
Box
>
</
Flex
>
</
Button
>
<
Button
isLoading=
{
isSubmitting
}
px=
"0"
onClick=
{
inputForm
.
handleSubmit
(
onSubmitApply
)
}
>
<
Flex
alignItems=
{
'center'
}
>
<
Box
px=
{
'1.25rem'
}
py=
{
'0.5rem'
}
>
{
t
(
'account_bill:confirm'
)
}
</
Box
>
</
Flex
>
</
Button
>
</
ModalFooter
>
</>
)
:
(
<>
<
ModalBody
>
<
Box
fontWeight=
{
500
}
fontSize=
{
'1rem'
}
pb=
{
'0.75rem'
}
>
{
t
(
'account_bill:support_wallet_apply_invoice'
)
}
</
Box
>
<
TableContainer
minH=
{
'50vh'
}
>
<
Table
>
<
Table
>
<
Thead
>
<
Thead
>
<
Tr
>
<
Tr
>
...
@@ -202,8 +254,8 @@ const ApplyInvoiceModal = ({ onClose }: { onClose: () => void }) => {
...
@@ -202,8 +254,8 @@ const ApplyInvoiceModal = ({ onClose }: { onClose: () => void }) => {
</
Flex
>
</
Flex
>
)
}
)
}
</
TableContainer
>
</
TableContainer
>
</
Box
>
</
ModalBody
>
<
Flex
pt=
{
'2.5rem'
}
justify=
{
'flex-end'
}
>
<
ModalFooter
>
<
Button
<
Button
variant=
{
'primary'
}
variant=
{
'primary'
}
px=
"0"
px=
"0"
...
@@ -221,57 +273,8 @@ const ApplyInvoiceModal = ({ onClose }: { onClose: () => void }) => {
...
@@ -221,57 +273,8 @@ const ApplyInvoiceModal = ({ onClose }: { onClose: () => void }) => {
</
Box
>
</
Box
>
</
Flex
>
</
Flex
>
</
Button
>
</
Button
>
</
Flex
>
</
ModalFooter
>
</
Box
>
</>
)
:
(
<
Box
px=
{
[
'1.6rem'
,
'3.25rem'
]
}
py=
{
[
'1rem'
,
'2rem'
]
}
>
<
Box
w=
{
'100%'
}
fontSize=
{
'0.875rem'
}
>
<
Flex
w=
{
'100%'
}
justifyContent=
{
'space-between'
}
>
<
Box
>
{
t
(
'account_bill:support_wallet_amount'
)
}
</
Box
>
<
Box
>
{
t
(
'account_bill:yuan'
,
{
amount
:
formatStorePrice2Read
(
totalPrice
)
})
}
</
Box
>
</
Flex
>
<
Box
w=
{
'100%'
}
py=
{
4
}
>
<
Divider
showBorderBottom=
{
false
}
/>
</
Box
>
</
Box
>
<
MyBox
isLoading=
{
isLoadingHeader
}
>
<
Flex
justify=
{
'center'
}
>
<
InvoiceHeaderSingleForm
inputForm=
{
inputForm
}
required
/>
</
Flex
>
</
MyBox
>
<
Flex
align=
{
'center'
}
w=
{
'19.8rem'
}
h=
{
'1.75rem'
}
mt=
{
4
}
px=
{
'0.75rem'
}
py=
{
'0.38rem'
}
bg=
{
'blue.50'
}
borderRadius=
{
'sm'
}
color=
{
'blue.600'
}
>
<
MyIcon
name=
"infoRounded"
w=
{
'14px'
}
h=
{
'14px'
}
/>
<
Box
ml=
{
2
}
fontSize=
{
'0.6875rem'
}
>
{
t
(
'account_bill:invoice_sending_info'
)
}
</
Box
>
</
Flex
>
<
Flex
justify=
{
'flex-end'
}
w=
{
'100%'
}
pt=
{
[
3
,
7
]
}
>
<
Button
variant=
{
'outline'
}
mr=
{
'0.75rem'
}
px=
"0"
onClick=
{
handleBack
}
>
<
Flex
alignItems=
{
'center'
}
>
<
Box
px=
{
'1.25rem'
}
py=
{
'0.5rem'
}
>
{
t
(
'account_bill:back'
)
}
</
Box
>
</
Flex
>
</
Button
>
<
Button
isLoading=
{
isSubmitting
}
px=
"0"
onClick=
{
inputForm
.
handleSubmit
(
onSubmitApply
)
}
>
<
Flex
alignItems=
{
'center'
}
>
<
Box
px=
{
'1.25rem'
}
py=
{
'0.5rem'
}
>
{
t
(
'account_bill:confirm'
)
}
</
Box
>
</
Flex
>
</
Button
>
</
Flex
>
</
Box
>
)
}
)
}
</
MyModal
>
</
MyModal
>
);
);
...
...
projects/app/src/pages/account/bill/components/InvoiceHeaderForm.tsx
View file @
a997f277
...
@@ -57,8 +57,7 @@ export const InvoiceHeaderSingleForm = ({
...
@@ -57,8 +57,7 @@ export const InvoiceHeaderSingleForm = ({
{
...
styles
}
{
...
styles
}
placeholder=
{
t
(
'account_bill:unit_code'
)
}
placeholder=
{
t
(
'account_bill:unit_code'
)
}
{
...
register
('
unifiedCreditCode
',
{
{
...
register
('
unifiedCreditCode
',
{
required
,
required
pattern
:
{
value
:
/^[
A
-
Z0
-9]{18}
$
/,
message
:
t
('
account_bill
:
unit_code_void
')
}
})}
})}
/>
/>
</
Flex
>
</
Flex
>
...
@@ -157,8 +156,7 @@ export const InvoiceHeaderSingleForm = ({
...
@@ -157,8 +156,7 @@ export const InvoiceHeaderSingleForm = ({
{
...
register
('
contactPhone
',
{
{
...
register
('
contactPhone
',
{
required
,
required
,
pattern
:
{
pattern
:
{
value
:
value
:
/^[1]{1}[0-9]{10}
$
/,
/^(?:\+?\
d
{1,3}[-
]?)?(?:\(\
d
{1,4}\)|\
d
{1,4})?[-
]?\
d
{1,4}[-
]?\
d
{1,4}[-
]?\
d
{1,9}
$
/,
message
:
t
('
account_bill
:
contact_phone_void
')
message
:
t
('
account_bill
:
contact_phone_void
')
}
}
})}
})}
...
...
projects/app/src/pages/login/components/LoginForm/components/FormLayout.tsx
View file @
a997f277
...
@@ -6,7 +6,7 @@ import { OAuthEnum } from '@fastgpt/global/support/user/constant';
...
@@ -6,7 +6,7 @@ import { OAuthEnum } from '@fastgpt/global/support/user/constant';
import
MyIcon
from
'@fastgpt/web/components/common/Icon'
;
import
MyIcon
from
'@fastgpt/web/components/common/Icon'
;
import
{
customAlphabet
}
from
'nanoid'
;
import
{
customAlphabet
}
from
'nanoid'
;
import
{
useRouter
}
from
'next/router'
;
import
{
useRouter
}
from
'next/router'
;
import
{
Dispatch
,
useEffect
,
useMemo
,
useRef
}
from
'react'
;
import
{
Dispatch
,
use
Callback
,
use
Effect
,
useMemo
,
useRef
}
from
'react'
;
import
{
useTranslation
}
from
'next-i18next'
;
import
{
useTranslation
}
from
'next-i18next'
;
import
I18nLngSelector
from
'@/components/Select/I18nLngSelector'
;
import
I18nLngSelector
from
'@/components/Select/I18nLngSelector'
;
import
{
useSystem
}
from
'@fastgpt/web/hooks/useSystem'
;
import
{
useSystem
}
from
'@fastgpt/web/hooks/useSystem'
;
...
@@ -23,7 +23,6 @@ interface Props {
...
@@ -23,7 +23,6 @@ interface Props {
const
FormLayout
=
({
children
,
setPageType
,
pageType
}:
Props
)
=>
{
const
FormLayout
=
({
children
,
setPageType
,
pageType
}:
Props
)
=>
{
const
{
t
}
=
useTranslation
();
const
{
t
}
=
useTranslation
();
const
router
=
useRouter
();
const
router
=
useRouter
();
const
{
toast
}
=
useToast
();
const
{
setLoginStore
,
feConfigs
}
=
useSystemStore
();
const
{
setLoginStore
,
feConfigs
}
=
useSystemStore
();
const
{
lastRoute
=
'/app/list'
}
=
router
.
query
as
{
lastRoute
:
string
};
const
{
lastRoute
=
'/app/list'
}
=
router
.
query
as
{
lastRoute
:
string
};
...
@@ -100,25 +99,23 @@ const FormLayout = ({ children, setPageType, pageType }: Props) => {
...
@@ -100,25 +99,23 @@ const FormLayout = ({ children, setPageType, pageType }: Props) => {
[feConfigs?.sso?.url, oAuthList.length]
[feConfigs?.sso?.url, oAuthList.length]
);
);
const formatUrl = useMemo(() => {
const onClickSso = useCallback(() => {
if (feConfigs?.sso?.url) {
if (!feConfigs?.sso?.url) return;
return `
$
{
feConfigs
.
sso
.
url
}
/login/
oauth
/
authorize
?
redirect_uri
=
$
{
encodeURIComponent
(
redirectUri
)}
&
state
=
$
{
state
.
current
}
`;
setLoginStore({
}
provider: OAuthEnum.sso,
return '';
lastRoute,
}, [feConfigs.sso, redirectUri]);
state: state.current
});
const url = `
$
{
feConfigs
.
sso
.
url
}
/login/
oauth
/
authorize
?
redirect_uri
=
$
{
encodeURIComponent
(
redirectUri
)}
&
state
=
$
{
state
.
current
}
`;
useEffect(() => {
window.open(url, '_self');
if (formatUrl && feConfigs?.sso?.autoLogin) {
}, [feConfigs?.sso?.url, lastRoute, redirectUri, setLoginStore]);
setLoginStore({
provider: OAuthEnum.sso,
lastRoute,
state: state.current
});
window.open(formatUrl, '_self');
useEffect(() => {
if (feConfigs?.sso?.autoLogin) {
onClickSso();
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [feConfigs?.sso?.autoLogin]);
}, [feConfigs.sso]);
return (
return (
<Flex flexDirection={'column'} h={'100%'}>
<Flex flexDirection={'column'} h={'100%'}>
...
@@ -186,22 +183,7 @@ const FormLayout = ({ children, setPageType, pageType }: Props) => {
...
@@ -186,22 +183,7 @@ const FormLayout = ({ children, setPageType, pageType }: Props) => {
h={'40px'}
h={'40px'}
borderRadius={'sm'}
borderRadius={'sm'}
leftIcon={<MyImage alt="" src={feConfigs.sso.icon as any} w="20px" />}
leftIcon={<MyImage alt="" src={feConfigs.sso.icon as any} w="20px" />}
onClick={() => {
onClick={onClickSso}
if (!formatUrl) {
toast({
title: 'SSO URL is not set',
status: 'error'
});
return;
}
setLoginStore({
provider: OAuthEnum.sso,
lastRoute,
state: state.current
});
window.open(formatUrl, '_self');
}}
>
>
{feConfigs.sso.title}
{feConfigs.sso.title}
</Button>
</Button>
...
...
projects/app/src/pages/login/index.tsx
View file @
a997f277
import
React
,
{
useState
,
useCallback
,
useEffect
}
from
'react'
;
import
React
,
{
useState
,
useCallback
,
useEffect
,
useMemo
}
from
'react'
;
import
{
import
{
Box
,
Box
,
Button
,
Button
,
...
@@ -73,7 +73,7 @@ const Login = ({ ChineseRedirectUrl }: { ChineseRedirectUrl: string }) => {
...
@@ -73,7 +73,7 @@ const Login = ({ ChineseRedirectUrl }: { ChineseRedirectUrl: string }) => {
[
lastRoute
,
router
,
setUserInfo
]
[
lastRoute
,
router
,
setUserInfo
]
);
);
function
DynamicComponent
({
type
}:
{
type
:
`
${
LoginPageTypeEnum
}
`
})
{
const
DynamicComponent
=
useMemo
(()
=>
{
const
TypeMap
=
{
const
TypeMap
=
{
[
LoginPageTypeEnum
.
passwordLogin
]:
LoginForm
,
[
LoginPageTypeEnum
.
passwordLogin
]:
LoginForm
,
[
LoginPageTypeEnum
.
register
]:
RegisterForm
,
[
LoginPageTypeEnum
.
register
]:
RegisterForm
,
...
@@ -81,10 +81,11 @@ const Login = ({ ChineseRedirectUrl }: { ChineseRedirectUrl: string }) => {
...
@@ -81,10 +81,11 @@ const Login = ({ ChineseRedirectUrl }: { ChineseRedirectUrl: string }) => {
[
LoginPageTypeEnum
.
wechat
]:
WechatForm
[
LoginPageTypeEnum
.
wechat
]:
WechatForm
};
};
const
Component
=
TypeMap
[
type
];
// @ts-ignore
const
Component
=
TypeMap
[
pageType
];
return
<
Component
setPageType=
{
setPageType
}
loginSuccess=
{
loginSuccess
}
/>;
return
<
Component
setPageType=
{
setPageType
}
loginSuccess=
{
loginSuccess
}
/>;
}
}
,
[
pageType
,
loginSuccess
]);
/* default login type */
/* default login type */
useEffect
(()
=>
{
useEffect
(()
=>
{
...
@@ -99,7 +100,7 @@ const Login = ({ ChineseRedirectUrl }: { ChineseRedirectUrl: string }) => {
...
@@ -99,7 +100,7 @@ const Login = ({ ChineseRedirectUrl }: { ChineseRedirectUrl: string }) => {
// init store
// init store
setLastChatAppId
(
''
);
setLastChatAppId
(
''
);
},
[
feConfigs
.
oauth
]);
},
[
feConfigs
?.
oauth
,
setLastChatAppId
]);
const
{
const
{
isOpen
:
isOpenRedirect
,
isOpen
:
isOpenRedirect
,
...
@@ -171,7 +172,7 @@ const Login = ({ ChineseRedirectUrl }: { ChineseRedirectUrl: string }) => {
...
@@ -171,7 +172,7 @@ const Login = ({ ChineseRedirectUrl }: { ChineseRedirectUrl: string }) => {
>
>
<
Box
w=
{
[
'100%'
,
'380px'
]
}
flex=
{
'1 0 0'
}
>
<
Box
w=
{
[
'100%'
,
'380px'
]
}
flex=
{
'1 0 0'
}
>
{
pageType
?
(
{
pageType
?
(
<
DynamicComponent
type=
{
pageType
}
/>
DynamicComponent
)
:
(
)
:
(
<
Center
w=
{
'full'
}
h=
{
'full'
}
position=
{
'relative'
}
>
<
Center
w=
{
'full'
}
h=
{
'full'
}
position=
{
'relative'
}
>
<
Loading
fixed=
{
false
}
/>
<
Loading
fixed=
{
false
}
/>
...
...
projects/app/src/web/context/useInitApp.ts
View file @
a997f277
...
@@ -71,7 +71,7 @@ export const useInitApp = () => {
...
@@ -71,7 +71,7 @@ export const useInitApp = () => {
if
(
sourceDomain
)
return
sourceDomain
;
if
(
sourceDomain
)
return
sourceDomain
;
return
document
.
referrer
;
return
document
.
referrer
;
})();
})();
console
.
log
(
formatSourceDomain
,
'-=-='
);
if
(
formatSourceDomain
&&
!
sessionStorage
.
getItem
(
'sourceDomain'
))
{
if
(
formatSourceDomain
&&
!
sessionStorage
.
getItem
(
'sourceDomain'
))
{
sessionStorage
.
setItem
(
'sourceDomain'
,
formatSourceDomain
);
sessionStorage
.
setItem
(
'sourceDomain'
,
formatSourceDomain
);
}
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment