diff --git a/packages/app/constants/settings/components/sessionItem/icons/chrome.jsx b/packages/app/constants/settings/components/sessionItem/icons/chrome.jsx new file mode 100644 index 00000000..c7a2a846 --- /dev/null +++ b/packages/app/constants/settings/components/sessionItem/icons/chrome.jsx @@ -0,0 +1,26 @@ +export default () => + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/app/constants/settings/components/sessionItem/icons/firefox.jsx b/packages/app/constants/settings/components/sessionItem/icons/firefox.jsx new file mode 100644 index 00000000..0c94a240 --- /dev/null +++ b/packages/app/constants/settings/components/sessionItem/icons/firefox.jsx @@ -0,0 +1,3 @@ +export default () => + + \ No newline at end of file diff --git a/packages/app/constants/settings/components/sessionItem/icons/mobile.jsx b/packages/app/constants/settings/components/sessionItem/icons/mobile.jsx new file mode 100644 index 00000000..43732704 --- /dev/null +++ b/packages/app/constants/settings/components/sessionItem/icons/mobile.jsx @@ -0,0 +1,34 @@ +export default () => { + return + + + +} \ No newline at end of file diff --git a/packages/app/constants/settings/components/sessionItem/index.jsx b/packages/app/constants/settings/components/sessionItem/index.jsx new file mode 100644 index 00000000..6d719daa --- /dev/null +++ b/packages/app/constants/settings/components/sessionItem/index.jsx @@ -0,0 +1,145 @@ +import React from "react" +import * as antd from "antd" +import SessionModel from "models/session" +import classnames from "classnames" + +import moment from "moment" +import UAParser from "ua-parser-js" + +import { Icons } from "components/Icons" + +import ChromeIcon from "./icons/chrome" +import MobileIcon from "./icons/mobile" +import FirefoxIcon from "./icons/firefox" + +import "./index.less" + +const DeviceIcon = (props) => { + if (!props.ua) { + return null + } + + if (props.ua.ua === "capacitor") { + return + } + + switch (props.ua.browser.name) { + case "Chrome": { + return + } + case "Firefox": { + return + } + default: { + return + } + } +} + +const SessionItem = (props) => { + const { session } = props + + const [collapsed, setCollapsed] = React.useState(true) + + const onClickCollapse = () => { + setCollapsed((prev) => { + return !prev + }) + } + + const onClickRevoke = () => { + // if (typeof props.onClickRevoke === "function") { + // props.onClickRevoke(session) + // } + } + + const isCurrentSession = React.useMemo(() => { + const currentUUID = SessionModel.session_uuid + return session.session_uuid === currentUUID + }) + + const ua = React.useMemo(() => { + return UAParser(session.client) + }) + + console.log(session, ua) + + return
+
+
+ +
+ + +
+
+

{session.session_uuid}

+
+ +
+
+ + + + {moment(session.date).format("DD/MM/YYYY HH:mm")} + +
+
+ + + + {session.ip_address} + +
+
+
+
+
+ +
+
+ + Revoke + +
+ +
+ + + + {session.location} + +
+ + { + ua.device.vendor &&
+ + + + {ua.device.vendor} | {ua.device.model} + +
+ } +
+
+} + +export default SessionItem \ No newline at end of file diff --git a/packages/app/constants/settings/components/sessionItem/index.less b/packages/app/constants/settings/components/sessionItem/index.less new file mode 100644 index 00000000..b11fab3a --- /dev/null +++ b/packages/app/constants/settings/components/sessionItem/index.less @@ -0,0 +1,167 @@ +@item_border_radius: 12px; + +.security_sessions_list_item_wrapper { + display: flex; + flex-direction: column; + + &.collapsed { + .security_sessions_list_item_extra-body { + height: 0; + transform: translateY(-100%); + padding-top: 0; + padding-bottom: 0; + } + + .security_sessions_list_item { + border-radius: @item_border_radius; + border-bottom-color: transparent; + } + } +} + + +.security_sessions_list_item { + position: relative; + + z-index: 100; + + display: flex; + flex-direction: row; + + align-items: center; + + border-radius: @item_border_radius @item_border_radius 0 0; + + border-bottom: 1px var(--border-color) solid; + + background-color: var(--background-color-primary); + + transition: all 150ms ease-in-out; + + padding: 5px; + + width: 100%; + + overflow: hidden; + + h1, + h2, + h3, + h4, + h5, + span { + margin: 0 !important; + } + + svg { + margin: 0 !important; + } + + gap: 10px; + + .security_sessions_list_item_icon { + display: inline-flex; + flex-direction: row; + + align-items: center; + justify-content: center; + + width: 50px; + height: 50px; + + padding: 10px; + + background-color: var(--background-color-accent); + + border-radius: 12px; + + svg { + width: 30px; + height: 30px; + } + + } + + .security_sessions_list_item_info { + display: flex; + flex-direction: column; + + gap: 5px; + + span { + user-select: text; + } + + .security_sessions_list_item_title { + display: inline-flex; + align-items: center; + + justify-content: space-between; + + font-size: 0.7rem; + font-weight: 600; + + gap: 20px; + } + + .security_sessions_list_item_info_details { + display: inline-flex; + flex-direction: row; + + align-items: center; + + gap: 7px; + } + } + + .security_sessions_list_item_actions { + display: flex; + flex-direction: row; + justify-content: flex-end; + + gap: 10px; + } +} + +.security_sessions_list_item_extra-body { + position: relative; + + z-index: 99; + + display: inline-flex; + flex-direction: row; + + align-items: center; + + width: 100%; + gap: 10px; + + background-color: var(--background-color-primary); + + transform: translateY(calc(-1 * calc(@item_border_radius / 2))); + + padding: 5px 10px; + padding-top: calc(calc(@item_border_radius / 2) + 5px); + + transition: all 150ms ease-in-out; + + border-radius: 0 0 @item_border_radius @item_border_radius; + + overflow: hidden; +} + +.security_sessions_list_item_info_details_item { + display: flex; + flex-direction: row; + + align-items: center; + + gap: 5px; + + font-size: 0.8rem; + font-weight: 400; + + svg { + margin: 0 !important; + } +} \ No newline at end of file diff --git a/packages/app/constants/settings/components/sessions/index.jsx b/packages/app/constants/settings/components/sessions/index.jsx index 54e20b4c..c7c0312d 100755 --- a/packages/app/constants/settings/components/sessions/index.jsx +++ b/packages/app/constants/settings/components/sessions/index.jsx @@ -1,13 +1,79 @@ import React from "react" -import { Button } from "antd" +import * as antd from "antd" +import SessionModel from "models/session" -export default (props) => { - return +import SessionItem from "../sessionItem" + +import "./index.less" + +export default () => { + const [loading, setLoading] = React.useState(true) + const [sessions, setSessions] = React.useState([]) + const [sessionsPage, setSessionsPage] = React.useState(1) + const [itemsPerPage, setItemsPerPage] = React.useState(3) + + const loadSessions = async () => { + setLoading(true) + + const response = await SessionModel.getAllSessions().catch((err) => { + console.error(err) + app.message.error("Failed to load sessions") + return null + }) + + console.log(response) + + if (response) { + setSessions(response) + } + + setLoading(false) + } + + const onClickRevoke = async (session) => { + console.log(session) + + app.message.warning("Not implemented yet") + } + + const onClickRevokeAll = async () => { + app.message.warning("Not implemented yet") + } + + React.useEffect(() => { + loadSessions() + }, []) + + if (loading) { + return + } + + const offset = (sessionsPage - 1) * itemsPerPage + const slicedItems = sessions.slice(offset, offset + itemsPerPage) + + return
+
+ { + slicedItems.map((session) => { + return + }) + } + + { + setSessionsPage(page) + }} + total={sessions.length} + showTotal={(total) => { + return `${total} Sessions` + }} + simple + + /> +
+
} \ No newline at end of file diff --git a/packages/app/constants/settings/components/sessions/index.less b/packages/app/constants/settings/components/sessions/index.less new file mode 100755 index 00000000..e123fe98 --- /dev/null +++ b/packages/app/constants/settings/components/sessions/index.less @@ -0,0 +1,40 @@ +.security_sessions { + display: flex; + flex-direction: column; + + width: 100%; + + .security_sessions_list { + display: flex; + flex-direction: column; + + justify-content: center; + + width: 100%; + + gap: 5px; + + .ant-pagination { + display: flex; + flex-direction: row; + + align-items: center; + + li { + display: flex; + flex-direction: row; + + align-items: center; + justify-content: center; + } + + button { + width: 24px!important; + + svg { + margin: 0 !important; + } + } + } + } +} \ No newline at end of file diff --git a/packages/app/constants/settings/security/index.jsx b/packages/app/constants/settings/security/index.jsx index 66e6e0d9..16c713e1 100755 --- a/packages/app/constants/settings/security/index.jsx +++ b/packages/app/constants/settings/security/index.jsx @@ -1,8 +1,4 @@ -import React from "react" import loadable from "@loadable/component" -import AuthModel from "models/auth" - -// TODO: Make logout button require a valid session to be not disabled export default { id: "security", @@ -33,7 +29,6 @@ export default { "description": "Manage your active sessions", "icon": "Monitor", "component": loadable(() => import("../components/sessions")), - "storaged": false } ] } \ No newline at end of file diff --git a/packages/app/src/pages/security/sessions/index.jsx b/packages/app/src/pages/security/sessions/index.jsx deleted file mode 100755 index 1c986d4a..00000000 --- a/packages/app/src/pages/security/sessions/index.jsx +++ /dev/null @@ -1,128 +0,0 @@ -import React from "react" -import * as antd from "antd" -import SessionModel from "models/session" -import moment from "moment" - -import { Icons } from "components/Icons" - -import "./index.less" - -const SessionItem = (props) => { - const { session } = props - - const [isCurrent, setIsCurrent] = React.useState(false) - - const onClickRevoke = () => { - if (typeof props.onClickRevoke === "function") { - props.onClickRevoke(session) - } - } - - React.useEffect(() => { - const currentUUID = SessionModel.session_uuid - - if (currentUUID === session.session_uuid) { - setIsCurrent(true) - } - }, []) - - return
-
-
-

{session.session_uuid}

- { - isCurrent && - Current - - } -
-
-
- {session.location} -
-
- - - - {moment(session.date).format("DD/MM/YYYY HH:mm")} - -
-
-
-
- - Revoke - -
-
-} - -export default () => { - const [loading, setLoading] = React.useState(true) - const [sessions, setSessions] = React.useState([]) - - const loadSessions = async () => { - setLoading(true) - - const response = await SessionModel.getAllSessions().catch((err) => { - console.error(err) - app.message.error("Failed to load sessions") - return null - }) - - console.log(response) - - if (response) { - setSessions(response) - } - - setLoading(false) - } - - const onClickRevoke = async (session) => { - console.log(session) - - app.message.warning("Not implemented yet") - } - - const onClickRevokeAll = async () => { - app.message.warning("Not implemented yet") - } - - React.useEffect(() => { - loadSessions() - }, []) - - if (loading) { - return - } - - return
-
-

Generated Sessions

- - Revoke all sessions - -
- -
- {sessions.map((session, index) => { - return - })} -
-
-} \ No newline at end of file diff --git a/packages/app/src/pages/security/sessions/index.less b/packages/app/src/pages/security/sessions/index.less deleted file mode 100755 index 3e8baff4..00000000 --- a/packages/app/src/pages/security/sessions/index.less +++ /dev/null @@ -1,96 +0,0 @@ -.sessions { - display: flex; - flex-direction: column; - - .sessions_header { - display: flex; - flex-direction: row; - justify-content: space-between; - - align-items: center; - - margin-bottom: 20px; - - h1 { - font-size: 1.5rem; - } - } - - .sessions_list { - display: flex; - flex-direction: column; - - align-items: center; - justify-content: center; - - width: 100%; - - .sessionItem { - display: flex; - flex-direction: column; - - padding: 10px; - - border-radius: 12px; - - background-color: var(--background-color-accent); - - margin-bottom: 10px; - - width: 35vw; - - h1, - h2, - h3, - h4, - h5, - span { - margin: 0 !important; - } - - .sessionItem_info { - display: flex; - flex-direction: column; - - gap: 10px; - - .sessionItem_info_title { - display: inline-flex; - align-items: center; - - justify-content: space-between; - - font-size: 1rem; - font-weight: 600; - } - - .sessionItem_info_details { - display: inline-flex; - flex-direction: row; - - align-items: center; - - font-size: 0.8rem; - font-weight: 400; - - .sessionItem_info_details_detail { - display: flex; - flex-direction: row; - - align-items: center; - - margin-right: 10px; - } - } - } - - .sessionItem_actions { - display: flex; - flex-direction: row; - justify-content: flex-end; - - gap: 10px; - } - } - } -} \ No newline at end of file