Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
f4cc465
Facebook direct posting works
marcusyi1 Dec 11, 2025
a42f5d8
build: facebook page schedule posting functionality
marcusyi1 Dec 13, 2025
7bc9170
build: database post history viewer
marcusyi1 Dec 19, 2025
825edc6
build: Facebook direct authentication
marcusyi1 Jan 2, 2026
700665f
build: scheduled photo
marcusyi1 Jan 8, 2026
8e31b17
fix: commit to help with push
marcusyi1 Jan 15, 2026
9cab43f
build: dark mode compatibility
marcusyi1 Jan 15, 2026
a26dc4a
Merge branch 'development' into Marcus-finishes-facebook-autoposter-fe
marcusyi1 Jan 15, 2026
90c520a
chore: standardize on npm, remove yarn.lock
marcusyi1 Jan 15, 2026
6ec267c
chore: sync yarn.lock
marcusyi1 Jan 15, 2026
b1c3761
chore: remove package-lock.json, standardize on yarn
marcusyi1 Jan 15, 2026
4dc3a7e
Resolve package-lock.json merge conflict
marcusyi1 Jan 15, 2026
d235fd6
fix: connecting facebook page to wireframe
marcusyi1 Jan 23, 2026
06d571a
Revert "fix: connecting facebook page to wireframe"
marcusyi1 Jan 24, 2026
bca806b
fix: facebook connection troubleshoot
marcusyi1 Feb 6, 2026
669ef9f
fix: routing issue
marcusyi1 Feb 7, 2026
b25bd2a
fix: resolve conflicts, keep Facebook autoposter logic
marcusyi1 Apr 11, 2026
29c7382
fix: resolve conflicts, keep Facebook autoposter logic
marcusyi1 Apr 11, 2026
8772a25
fix: prettier formatting in SocialMediaComposer
marcusyi1 Apr 11, 2026
baf9416
Merge origin/development into Marcus-finishes-facebook-autoposter-fe
marcusyi1 Apr 17, 2026
247b5e2
Add validator package via yarn
marcusyi1 Apr 17, 2026
e942856
Refactor: reduce cognitive complexity in FacebookConnection and Socia…
marcusyi1 Apr 17, 2026
5ef3f1c
Fix prettier formatting in SocialMediaComposer
marcusyi1 Apr 17, 2026
a9e6e67
Refactor: fix nesting depth in facebookAuthActions and cognitive comp…
marcusyi1 Apr 18, 2026
fd30d11
Fix: prettier formatting FacebookConnection
marcusyi1 Apr 18, 2026
7fa0614
Refactor: extract PageSelectorModal and helpers to reduce cognitive c…
marcusyi1 Apr 18, 2026
95ff941
Refactor: reduce SocialMediaComposer cognitive complexity from 57 to <15
marcusyi1 Apr 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@
"react-to-print": "^3.0.5",
"react-toastify": "^5.3.1",
"react-tooltip": "^4.5.1",
"react-transition-group": "^4.4.5",
"react-use-websocket": "^3.0.0",
"react-window": "^1.8.11",
"reactjs-popup": "^2.0.5",
Expand All @@ -127,7 +128,7 @@
"tinymce": "^7.2.0",
"util": "^0.12.5",
"uuid": "^9.0.1",
"validator": "^13.15.26",
"validator": "^13.15.35",
"webpack": "^5.104.1"
},
"resolutions": {
Expand Down Expand Up @@ -177,6 +178,7 @@
"@types/react-router-dom": "^5.3.3",
"@typescript-eslint/eslint-plugin": "^8.44.1",
"@typescript-eslint/parser": "^8.44.1",
"@vitejs/plugin-basic-ssl": "^2.1.0",
"@vitejs/plugin-react": "^4.5.0",
"@vitest/ui": "3.2.2",
"babel-jest": "^29.7.0",
Expand Down
45 changes: 28 additions & 17 deletions src/App.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,20 @@ html {

body {
display: grid;
grid-template-columns: auto 0px;
grid-template-columns: auto 0;
}

button {
border: none;
background-color: transparent;
}

#root {
height: 100%;
overflow-x: auto;
overflow-y: hidden;
overflow: auto hidden;

/* background-color: var(--background-color); */
background-color: #ffffff;
background-color: #fff;
}

.App {
Expand Down Expand Up @@ -46,14 +48,14 @@ button {
width: 180px;
}

.accordion-toggle:after {
font-family: 'FontAwesome', sans-serif;
.accordion-toggle::after {
font-family: FontAwesome, sans-serif;
content: '\f054';
float: right;
}

.accordion-toggle.collapsed:after {
/* symbol for down chevron*/
.accordion-toggle.collapsed::after {
/* symbol for down chevron */
content: '\f078';
}

Expand All @@ -63,27 +65,31 @@ button {
}

.hgn-leaderboard {
/*background-color:#FFCCBC */
/* background-color:#FFCCBC */
background-color: black;
}

.bg-super {
background-color: #0000cd !important;
/* medium blue #0000CD*/

/* medium blue #0000CD */
}

.bg-awesome {
background-color: #990099 !important;
background-color: #909 !important;

/* violet */
}

.bg-super-awesome {
background-color: #660099 !important;
background-color: #609 !important;

/* purple */
}

.bg-orange {
background-color: #ffa500 !important;

/* brighter orange */
}

Expand All @@ -92,12 +98,14 @@ button {
}

.bg-bright-red {
background-color: #ff3300 !important;
background-color: #f30 !important;

/* bright-red */
}

.bg-almost-red {
background-color: #cc3366 !important;
background-color: #c36 !important;

/* 90%to100%-red */
}

Expand Down Expand Up @@ -142,7 +150,7 @@ button {
}

.bg-darkmode-black {
background-color: #000000 !important;
background-color: #000 !important;
}

.bg-azure {
Expand Down Expand Up @@ -170,7 +178,7 @@ button {
}

.text-custom-grey {
color: rgb(192, 192, 192) !important;
color: rgb(192 192 192) !important;
}

.box-shadow-dark {
Expand All @@ -194,7 +202,7 @@ button {
}

nav ul.navbar-nav li:last-child > div {
transform: translate3d(-26px, 40px, 0px) !important;
transform: translate3d(-26px, 40px, 0) !important;
}

.calendar-icon-dark {
Expand All @@ -205,7 +213,9 @@ nav ul.navbar-nav li:last-child > div {
text-decoration: none;
padding: 10px;
margin: 10px;

--offset: 100px;

margin-top: calc(100vh + var(--offset));
font-family: sans-serif;
color: #fff;
Expand Down Expand Up @@ -273,6 +283,7 @@ input[type='checkbox']:not([disabled]):hover {
.pdrl-1 {
padding: 0 1em !important;
}

.user-card-container {
display: flex;
flex-wrap: wrap;
Expand Down
196 changes: 196 additions & 0 deletions src/actions/facebookActions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
import axios from 'axios';
import { toast } from 'react-toastify';
import { ENDPOINTS } from '~/utils/URL';

export const postFacebookContent = ({ message, link, imageUrl, pageId, requestor }) => async () => {
try {
const payload = {
message,
link,
imageUrl,
pageId,
requestor,
};

const { data } = await axios.post(ENDPOINTS.FACEBOOK_POST, payload);
toast.success('Facebook post created.');
return data;
} catch (error) {
const detail =
error.response?.data?.details ||
error.response?.data?.error ||
error.response?.data ||
error.message;
toast.error(`Failed to post to Facebook: ${detail}`);
throw error;
}
};

export const scheduleFacebookPost =
({ message, scheduledFor, timezone = 'America/Los_Angeles', link, imageUrl, pageId, requestor }) =>
async () => {
try {
const payload = {
message,
scheduledFor,
timezone,
link,
imageUrl,
pageId,
requestor,
};

const { data } = await axios.post(ENDPOINTS.FACEBOOK_SCHEDULE_POST, payload);
toast.success('Facebook post scheduled.');
return data;
} catch (error) {
const detail =
error.response?.data?.details ||
error.response?.data?.error ||
error.response?.data ||
error.message;
toast.error(`Failed to schedule Facebook post: ${detail}`);
throw error;
}
};

/**
* Schedules a Facebook post with direct image file upload
*/
export const scheduleFacebookPostWithImage = (formData) => async () => {
try {
const { data } = await axios.post(ENDPOINTS.FACEBOOK_SCHEDULE_POST_UPLOAD, formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
timeout: 60000,
});
toast.success('Facebook post scheduled.');
return data;
} catch (error) {
const detail =
error.response?.data?.details ||
error.response?.data?.error ||
error.response?.data ||
error.message;
toast.error(`Failed to schedule Facebook post: ${detail}`);
throw error;
}
};

// ============================================
// NEW ACTIONS
// ============================================

/**
* Fetches pending/sending scheduled posts
*/
export const fetchScheduledPosts =
({ requestor, status, limit = 50, skip = 0 }) =>
async () => {
try {
const params = new URLSearchParams();
if (status) params.append('status', status);
params.append('limit', limit);
params.append('skip', skip);
if (requestor) {
params.append('requestor', JSON.stringify(requestor));
}

const { data } = await axios.get(`${ENDPOINTS.FACEBOOK_SCHEDULED}?${params.toString()}`);
return data;
} catch (error) {
const detail =
error.response?.data?.details || error.response?.data?.error || error.message;
toast.error(`Failed to fetch scheduled posts: ${detail}`);
throw error;
}
};

/**
* Fetches post history (MongoDB sent posts + Facebook Graph API posts)
*/
export const fetchPostHistory =
({ requestor, limit = 25, source = 'all', pageId, status, postMethod }) =>
async () => {
try {
const params = new URLSearchParams();
params.append('limit', limit);
params.append('source', source);
if (pageId) params.append('pageId', pageId);
if (status) params.append('status', status);
if (postMethod) params.append('postMethod', postMethod);
if (requestor) {
params.append('requestor', JSON.stringify(requestor));
}

const { data } = await axios.get(`${ENDPOINTS.FACEBOOK_HISTORY}?${params.toString()}`);
return data;
} catch (error) {
const detail =
error.response?.data?.details || error.response?.data?.error || error.message;
toast.error(`Failed to fetch post history: ${detail}`);
throw error;
}
};

/**
* Cancels a pending scheduled post
*/
export const cancelScheduledPost =
({ postId, requestor }) =>
async () => {
try {
const { data } = await axios.delete(`${ENDPOINTS.FACEBOOK_SCHEDULE}/${postId}`, {
data: { requestor },
});
toast.success('Scheduled post cancelled.');
return data;
} catch (error) {
const detail =
error.response?.data?.details || error.response?.data?.error || error.message;
toast.error(`Failed to cancel scheduled post: ${detail}`);
throw error;
}
};

/**
* Updates a pending scheduled post
*/
export const updateScheduledPost =
({ postId, message, scheduledFor, timezone, link, imageUrl, requestor }) =>
async () => {
try {
const payload = { message, scheduledFor, timezone, link, imageUrl, requestor };
const { data } = await axios.put(`${ENDPOINTS.FACEBOOK_SCHEDULE}/${postId}`, payload);
toast.success('Scheduled post updated.');
return data;
} catch (error) {
const detail =
error.response?.data?.details || error.response?.data?.error || error.message;
toast.error(`Failed to update scheduled post: ${detail}`);
throw error;
}
};

export const postFacebookContentWithImage = (formData) => async () => {
try {
const { data } = await axios.post(ENDPOINTS.FACEBOOK_POST_UPLOAD, formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
// Increase timeout for larger files
timeout: 60000,
});
toast.success('Facebook post created.');
return data;
} catch (error) {
const detail =
error.response?.data?.details ||
error.response?.data?.error ||
error.response?.data ||
error.message;
toast.error(`Failed to post to Facebook: ${detail}`);
throw error;
}
};
Loading
Loading