Skip to content

Commit 0294751

Browse files
committed
Refactor code structure for improved readability and maintainability
1 parent 7dee7c9 commit 0294751

File tree

5 files changed

+283
-30
lines changed

5 files changed

+283
-30
lines changed

.vscode/settings.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"fileupload",
3939
"Foxbiz",
4040
"gapis",
41+
"hangingpiece",
4142
"hideloading",
4243
"hintrc",
4344
"hljs",
Lines changed: 109 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,114 @@
11
import './style.scss';
2+
import hpLogo from 'res/hp-logo.png';
23

3-
const BETTERKEEP_LOGO = 'https://betterkeep.app/icons/logo.png';
4-
const BETTERKEEP_URL = 'https://betterkeep.app/welcome?utm_source=acode_announcement_banner';
4+
const BANNERS = [
5+
{
6+
id: 'betterkeep',
7+
logo: 'https://betterkeep.app/icons/logo.png',
8+
url: 'https://betterkeep.app/welcome?utm_source=acode_announcement_banner',
9+
alt: 'Better Keep Notes',
10+
badge: 'NEW',
11+
title: 'Introducing Better Keep Notes',
12+
subtitle: 'A beautiful, private note-taking app. Try it free!',
13+
cta: 'Learn More',
14+
theme: 'betterkeep',
15+
},
16+
{
17+
id: 'hangingpiece',
18+
logo: hpLogo,
19+
url: 'https://www.hangingpiece.com?utm_source=acode_announcement_banner',
20+
alt: 'Hanging Piece',
21+
badge: 'NEW',
22+
title: 'Hanging Piece - AI Chess Coach',
23+
subtitle: 'Understand WHY you blundered. Stop repeating mistakes!',
24+
cta: 'Try Now',
25+
theme: 'hangingpiece',
26+
},
27+
];
528

6-
export default () => (
7-
<a href={BETTERKEEP_URL} target='_blank' rel='noopener noreferrer' className='announcement-banner'>
8-
<div className='announcement-banner__content'>
9-
<div className='announcement-banner__logo-wrapper'>
10-
<img src={BETTERKEEP_LOGO} alt='Better Keep Notes' className='announcement-banner__logo' />
11-
</div>
12-
<div className='announcement-banner__text'>
13-
<span className='announcement-banner__new-badge'>NEW</span>
14-
<span className='announcement-banner__title'>Introducing Better Keep Notes</span>
15-
<span className='announcement-banner__subtitle'>A beautiful, private note-taking app. Try it free!</span>
29+
const TOGGLE_INTERVAL = 8000;
30+
31+
export default function AnnouncementBanner() {
32+
let currentIndex = 0;
33+
34+
const $logo = <img alt='App Logo' className='announcement-banner__logo' />;
35+
const $badge = <span className='announcement-banner__new-badge' />;
36+
const $title = <span className='announcement-banner__title' />;
37+
const $subtitle = <span className='announcement-banner__subtitle' />;
38+
const $ctaText = <span className='announcement-banner__cta-text' />;
39+
40+
const $link = (
41+
// biome-ignore lint/a11y/useValidAnchor: URL is dynamic
42+
<a target='_blank' rel='noopener noreferrer' className='announcement-banner'>
43+
<div className='announcement-banner__content'>
44+
<div className='announcement-banner__logo-wrapper'>{$logo}</div>
45+
<div className='announcement-banner__text'>
46+
{$badge}
47+
{$title}
48+
{$subtitle}
49+
</div>
50+
<div className='announcement-banner__cta'>
51+
{$ctaText}
52+
<span className='announcement-banner__arrow'></span>
53+
</div>
1654
</div>
17-
<div className='announcement-banner__cta'>
18-
<span className='announcement-banner__cta-text'>Learn More</span>
19-
<span className='announcement-banner__arrow'></span>
55+
<div className='announcement-banner__indicators'>
56+
{BANNERS.map((_, index) => (
57+
<span className={`announcement-banner__indicator ${index === 0 ? 'active' : ''}`} data-index={index} />
58+
))}
2059
</div>
21-
</div>
22-
</a>
23-
);
60+
</a>
61+
);
62+
63+
const $content = $link.querySelector('.announcement-banner__content');
64+
65+
const updateBanner = (index, animate = false) => {
66+
const b = BANNERS[index];
67+
68+
const applyContent = () => {
69+
$link.href = b.url;
70+
$link.className = `announcement-banner announcement-banner--${b.theme}`;
71+
$logo.src = b.logo;
72+
$logo.alt = b.alt;
73+
$badge.textContent = b.badge;
74+
$title.textContent = b.title;
75+
$subtitle.textContent = b.subtitle;
76+
$ctaText.textContent = b.cta;
77+
78+
// Update indicators
79+
$link.querySelectorAll('.announcement-banner__indicator').forEach((indicator, i) => {
80+
indicator.classList.toggle('active', i === index);
81+
});
82+
};
83+
84+
if (animate && $content) {
85+
// Slide out
86+
$content.classList.add('slide-out');
87+
$content.classList.remove('slide-in');
88+
89+
setTimeout(() => {
90+
applyContent();
91+
// Slide in
92+
$content.classList.remove('slide-out');
93+
$content.classList.add('slide-in');
94+
95+
setTimeout(() => {
96+
$content.classList.remove('slide-in');
97+
}, 300);
98+
}, 300);
99+
} else {
100+
applyContent();
101+
}
102+
};
103+
104+
// Initialize with first banner
105+
updateBanner(0);
106+
107+
// Toggle banners periodically
108+
setInterval(() => {
109+
currentIndex = (currentIndex + 1) % BANNERS.length;
110+
updateBanner(currentIndex, true);
111+
}, TOGGLE_INTERVAL);
112+
113+
return $link;
114+
}

client/components/announcementBanner/style.scss

Lines changed: 88 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
.announcement-banner {
2+
z-index: 9;
3+
margin: 0;
24
display: flex;
35
align-items: center;
46
justify-content: center;
@@ -17,7 +19,30 @@
1719
cursor: pointer;
1820
overflow: hidden;
1921
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
20-
transition: all 0.3s ease;
22+
transition: background 0.5s ease, background-size 0.3s ease;
23+
24+
// Hanging Piece theme - chess-inspired dark green
25+
&--hangingpiece {
26+
background: linear-gradient(135deg,
27+
#1a1a1a 0%,
28+
#2d2d2d 25%,
29+
#1e3a2f 50%,
30+
#1a1a1a 100%);
31+
32+
.announcement-banner__new-badge {
33+
background: #009966;
34+
box-shadow: 0 2px 10px rgba(0, 153, 102, 0.4);
35+
}
36+
37+
.announcement-banner__cta {
38+
background: #009966;
39+
box-shadow: 0 4px 15px rgba(0, 153, 102, 0.3);
40+
}
41+
42+
.announcement-banner__logo-wrapper {
43+
background: rgba(0, 153, 102, 0.15);
44+
}
45+
}
2146

2247
&::before {
2348
content: '';
@@ -73,7 +98,41 @@
7398
}
7499
}
75100

101+
@keyframes slideOutLeft {
102+
0% {
103+
opacity: 1;
104+
transform: translateX(0);
105+
}
106+
107+
100% {
108+
opacity: 0;
109+
transform: translateX(-30px);
110+
}
111+
}
112+
113+
@keyframes slideInRight {
114+
0% {
115+
opacity: 0;
116+
transform: translateX(30px);
117+
}
118+
119+
100% {
120+
opacity: 1;
121+
transform: translateX(0);
122+
}
123+
}
124+
76125
&__content {
126+
transition: opacity 0.3s ease, transform 0.3s ease;
127+
128+
&.slide-out {
129+
animation: slideOutLeft 0.3s ease forwards;
130+
}
131+
132+
&.slide-in {
133+
animation: slideInRight 0.3s ease forwards;
134+
}
135+
77136
display: flex;
78137
align-items: center;
79138
gap: 16px;
@@ -90,14 +149,14 @@
90149
border-radius: 10px;
91150
background: rgba(255, 255, 255, 0.1);
92151
backdrop-filter: blur(10px);
93-
padding: 4px;
152+
// padding: 4px;
94153
flex-shrink: 0;
95154
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
96155
}
97156

98157
&__logo {
99-
width: 28px;
100-
height: 28px;
158+
width: 100%;
159+
height: 100%;
101160
object-fit: contain;
102161
border-radius: 6px;
103162
}
@@ -160,6 +219,29 @@
160219
transition: transform 0.3s ease;
161220
}
162221

222+
&__indicators {
223+
position: absolute;
224+
bottom: 4px;
225+
left: 50%;
226+
transform: translateX(-50%);
227+
display: flex;
228+
gap: 6px;
229+
z-index: 2;
230+
}
231+
232+
&__indicator {
233+
width: 6px;
234+
height: 6px;
235+
border-radius: 50%;
236+
background: rgba(255, 255, 255, 0.3);
237+
transition: all 0.3s ease;
238+
239+
&.active {
240+
background: rgba(255, 255, 255, 0.9);
241+
transform: scale(1.2);
242+
}
243+
}
244+
163245
// Responsive styles
164246
@media screen and (max-width: 768px) {
165247
padding: 10px 16px;
@@ -215,8 +297,8 @@
215297
}
216298

217299
&__logo {
218-
width: 20px;
219-
height: 20px;
300+
width: 100%;
301+
height: 100%;
220302
}
221303

222304
&__new-badge {

client/res/hp-logo.png

1.32 MB
Loading

server/reportsCli.js

Lines changed: 85 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,95 @@
11
/* eslint-disable no-console */
22
const downloadGrCsv = require('./lib/downloadSalesCsv');
33

4-
const month = process.argv[2];
5-
const year = process.argv[3];
6-
const type = process.argv[4] || 'sales';
4+
const args = process.argv.slice(2);
75

8-
if (!month || !year) {
9-
console.error('Usage: node reportsCli.js <mm> <yyyy> <sales|earnings>');
6+
// Month name mappings (short and full)
7+
const monthNames = {
8+
jan: 1,
9+
january: 1,
10+
feb: 2,
11+
february: 2,
12+
mar: 3,
13+
march: 3,
14+
apr: 4,
15+
april: 4,
16+
may: 5,
17+
jun: 6,
18+
june: 6,
19+
jul: 7,
20+
july: 7,
21+
aug: 8,
22+
august: 8,
23+
sep: 9,
24+
september: 9,
25+
oct: 10,
26+
october: 10,
27+
nov: 11,
28+
november: 11,
29+
dec: 12,
30+
december: 12,
31+
};
32+
33+
/**
34+
* Parse month input - accepts number (1-12) or name (jan, january, etc.)
35+
*/
36+
function parseMonth(input) {
37+
if (!input) return null;
38+
const lower = input.toLowerCase();
39+
if (monthNames[lower]) return monthNames[lower];
40+
const num = Number(input);
41+
if (num >= 1 && num <= 12) return num;
42+
return null;
43+
}
44+
45+
// Show help if -h or --help is passed
46+
if (args.includes('-h') || args.includes('--help')) {
47+
console.log(`
48+
Reports CLI - Download sales or earnings reports
49+
50+
USAGE:
51+
node reportsCli.js <month> <year> [type]
52+
53+
ARGUMENTS:
54+
<month> Month number (1-12) or name (jan, january, feb, february, etc.)
55+
<year> Full year (e.g., 2025)
56+
[type] Report type: 'sales' or 'earnings' (default: sales)
57+
58+
OPTIONS:
59+
-h, --help Show this help message
60+
61+
MONTH NAMES:
62+
Short: jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec
63+
Full: january, february, march, april, may, june, july,
64+
august, september, october, november, december
65+
66+
EXAMPLES:
67+
node reportsCli.js jan 2025 # Download sales report for January 2025
68+
node reportsCli.js january 2025 # Same as above
69+
node reportsCli.js 01 2025 # Same as above (using number)
70+
node reportsCli.js dec 2025 earnings # Download earnings report for December 2025
71+
`);
72+
process.exit(0);
73+
}
74+
75+
const monthInput = args[0];
76+
const year = args[1];
77+
const type = args[2] || 'sales';
78+
79+
if (!monthInput || !year) {
80+
console.error('Usage: node reportsCli.js <month> <year> [type]');
81+
console.error('Run with -h or --help for more information.');
82+
process.exit(1);
83+
}
84+
85+
const month = parseMonth(monthInput);
86+
if (!month) {
87+
console.error(`Invalid month: "${monthInput}"`);
88+
console.error('Use a number (1-12) or name (jan, january, feb, february, etc.)');
1089
process.exit(1);
1190
}
1291

13-
downloadGrCsv(Number(year), Number(month), type)
92+
downloadGrCsv(Number(year), month, type)
1493
.then((filePath) => {
1594
console.log(`Report downloaded to ${filePath}`);
1695
process.exit(0);

0 commit comments

Comments
 (0)