Skip to content
This repository was archived by the owner on Jun 15, 2026. It is now read-only.

Commit a6bdd10

Browse files
RMCamposCopilot
andauthored
feat: add last activity date time (#57)
* feat: add last activity date time * chore: update missing files * chore: improve last seen message to inclute timezone * chore: remove the formatting from backend * chore: fix client issues * fix: add utd to formatting in the client * fix lint issue * chore: fix lastLogin always showing the current login * fix: address sidebar and sign-in review comments Agent-Logs-Url: https://github.com/RMCampos/tasknote/sessions/04a13c25-87ea-44cc-ac53-cfefd16e0262 Co-authored-by: RMCampos <2219519+RMCampos@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: RMCampos <2219519+RMCampos@users.noreply.github.com>
1 parent 9bf0f40 commit a6bdd10

18 files changed

Lines changed: 128 additions & 25 deletions

File tree

client/src/components/Sidebar/index.tsx

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useContext, useEffect } from 'react';
1+
import React, { useContext, useEffect, useState } from 'react';
22
import { Nav } from 'react-bootstrap';
33
import { NavLink } from 'react-router';
44
import { useTranslation } from 'react-i18next';
@@ -22,6 +22,7 @@ interface Props {
2222
function Sidebar(props: React.PropsWithChildren<Props>): React.ReactNode {
2323
const { signOut, user } = useContext(AuthContext);
2424
const { currentPage, setNewPage } = useContext(SidebarContext);
25+
const [lastSeen, setLastSeen] = useState('');
2526
const { t } = useTranslation();
2627
const build = `Build: ${env.VITE_BUILD}`;
2728

@@ -46,7 +47,25 @@ function Sidebar(props: React.PropsWithChildren<Props>): React.ReactNode {
4647
return '';
4748
};
4849

49-
useEffect(() => {}, [user, currentPage]);
50+
useEffect(() => {
51+
if (user && user.lastLogin) {
52+
const utcString = user.lastLogin.endsWith('Z') ? user.lastLogin : `${user.lastLogin}Z`;
53+
const date = new Date(utcString);
54+
if (Number.isNaN(date.getTime())) {
55+
setLastSeen('');
56+
return;
57+
}
58+
const fmtted = date.toLocaleString(navigator.language, {
59+
timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
60+
day: '2-digit',
61+
month: '2-digit',
62+
year: 'numeric',
63+
hour: '2-digit',
64+
minute: '2-digit'
65+
});
66+
setLastSeen(fmtted);
67+
}
68+
}, [user]);
5069

5170
return (
5271
<>
@@ -95,9 +114,12 @@ function Sidebar(props: React.PropsWithChildren<Props>): React.ReactNode {
95114

96115
{/* Footer at the bottom */}
97116
<div className="mt-auto text-center text-muted py-3">
98-
<small data-testid="footer-text">
99-
{build}
100-
</small>
117+
{lastSeen && (
118+
<div>
119+
<small>{t('sidebar_last_seen', { time: lastSeen })}</small>
120+
</div>
121+
)}
122+
<small data-testid="footer-text">{build}</small>
101123
</div>
102124
</div>
103125

client/src/constants/english.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ const enTranslations = {
187187
account_delete_btn: 'Yes, delete everything',
188188

189189
footer_my_account: 'My Account ',
190+
sidebar_last_seen: 'Last seen {{time}}',
190191

191192
logout: 'Logout'
192193
};

client/src/constants/portuguese.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ const ptBrTranslations = {
188188
account_delete_btn: 'Sim, deletar tudo',
189189

190190
footer_my_account: 'Minha Conta ',
191+
sidebar_last_seen: 'Último acesso {{time}}',
191192

192193
logout: 'Sair'
193194
};

client/src/constants/russian.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ const ruTranslations = {
187187
account_delete_btn: 'Да, удалить все',
188188

189189
footer_my_account: 'Мой аккаунт ',
190+
sidebar_last_seen: 'Последний вход {{time}}',
190191

191192
logout: 'Выйти'
192193
};

client/src/constants/spanish.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ const esTranslations = {
187187
account_delete_btn: 'Sí, borra todo',
188188

189189
footer_my_account: 'Mi Cuenta ',
190+
sidebar_last_seen: 'Último acceso {{time}}',
190191

191192
logout: 'Cerrar sesión'
192193
};

client/src/context/AuthProvider.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,8 @@ const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children }: Pro
9797
admin: registerResponse.admin,
9898
createdAt: new Date(registerResponse.createdAt),
9999
gravatarImageUrl: registerResponse.gravatarImageUrl,
100-
lang: registerResponse.lang
100+
lang: registerResponse.lang,
101+
lastLogin: registerResponse.lastLogin
101102
};
102103

103104
setSigned(true);

client/src/types/SigninResponse.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ export type SignInResponse = {
77
token: string;
88
gravatarImageUrl: string;
99
lang: string;
10+
lastLogin: string;
1011
};

client/src/types/UserResponse.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ export type UserResponse = {
66
createdAt: Date;
77
gravatarImageUrl: string;
88
lang: string;
9+
lastLogin: string;
910
};

server/src/main/java/br/com/tasknoteapp/server/entity/UserEntity.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ public class UserEntity implements UserDetails {
5858
@Column(name = "last_password_change", nullable = false)
5959
private LocalDateTime lastPasswordChange;
6060

61+
@Column(name = "last_login")
62+
private LocalDateTime lastLogin;
63+
6164
@Override
6265
public Collection<? extends GrantedAuthority> getAuthorities() {
6366
return List.of();
@@ -173,4 +176,12 @@ public LocalDateTime getLastPasswordChange() {
173176
public void setLastPasswordChange(LocalDateTime lastPasswordChange) {
174177
this.lastPasswordChange = lastPasswordChange;
175178
}
179+
180+
public LocalDateTime getLastLogin() {
181+
return lastLogin;
182+
}
183+
184+
public void setLastLogin(LocalDateTime lastLogin) {
185+
this.lastLogin = lastLogin;
186+
}
176187
}

server/src/main/java/br/com/tasknoteapp/server/response/UserResponse.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ public record UserResponse(
1111
Boolean admin,
1212
LocalDateTime createdAt,
1313
LocalDateTime inactivatedAt,
14+
LocalDateTime lastLogin,
1415
String gravatarImageUrl) {
1516

1617
/**
@@ -27,6 +28,7 @@ public static UserResponse fromEntity(UserEntity user, String gravatarUrl) {
2728
user.getAdmin(),
2829
user.getCreatedAt(),
2930
user.getInactivatedAt(),
31+
user.getLastLogin(),
3032
gravatarUrl);
3133
}
3234
}

0 commit comments

Comments
 (0)