diff --git a/.github/workflows/deploy_api.yml b/.github/workflows/deploy_api.yml
index 3e2e3c59..9df543c0 100644
--- a/.github/workflows/deploy_api.yml
+++ b/.github/workflows/deploy_api.yml
@@ -49,7 +49,7 @@ jobs:
echo "Starting redis service..."
sudo systemctl start redis
echo "Starting gunicorn..."
- nohup sh -c 'gunicorn -w 4 marketplace.wsgi:application > gunicorn.out 2>&1' &
+ nohup sh -c 'gunicorn -w 4 --access-logfile - --error-logfile - --timeout 300 marketplace.wsgi:application > gunicorn.out 2>&1' &
echo "Gunicorn started."
echo "Starting celery worker..."
nohup sh -c 'celery -A marketplace worker -l info > celery_worker.out 2>&1' &
diff --git a/.github/workflows/deploy_prod_api.yml b/.github/workflows/deploy_prod_api.yml
index edbdf38b..e5b4bf3c 100644
--- a/.github/workflows/deploy_prod_api.yml
+++ b/.github/workflows/deploy_prod_api.yml
@@ -51,7 +51,7 @@ jobs:
echo "Starting redis service..."
sudo systemctl start redis
echo "Starting gunicorn..."
- nohup sh -c 'gunicorn -w 4 marketplace.wsgi:application > gunicorn.out 2>&1' &
+ nohup sh -c 'gunicorn -w 4 --access-logfile - --error-logfile - --timeout 300 marketplace.wsgi:application > gunicorn.out 2>&1' &
echo "Gunicorn started."
echo "Starting celery worker..."
nohup sh -c 'celery -A marketplace worker -l info > celery_worker.out 2>&1' &
diff --git a/.github/workflows/deploy_ui.yml b/.github/workflows/deploy_ui.yml
index b3f86279..c6146860 100644
--- a/.github/workflows/deploy_ui.yml
+++ b/.github/workflows/deploy_ui.yml
@@ -42,7 +42,7 @@ jobs:
npm install
npm run build
sudo systemctl restart nginx
- pm2 stop marketplace
+ pm2 delete marketplace
pm2 start npm --name marketplace -- start
- name: Remove Github Actions IP from security group
diff --git a/src/api/marketplace/accounts/serializers.py b/src/api/marketplace/accounts/serializers.py
index 394d9494..2bc6f3b5 100644
--- a/src/api/marketplace/accounts/serializers.py
+++ b/src/api/marketplace/accounts/serializers.py
@@ -25,46 +25,6 @@
from django.shortcuts import get_object_or_404
from django.db.models import Avg
-
-class BusinessAccountMetaDataSerializer(serializers.ModelSerializer):
- influencer_ids = serializers.SerializerMethodField(read_only=True)
- is_twitter_connected = serializers.SerializerMethodField(read_only=True)
- is_wallet_connected = serializers.SerializerMethodField(read_only=True)
-
- class Meta:
- model = BusinessAccountMetaData
- fields = "__all__"
-
- def get_influencer_ids(self, business_meta_data):
- completed_orders = Order.objects.filter(
- buyer=business_meta_data.user_account, status="completed"
- )
- influencer_ids = set()
-
- for completed_order in completed_orders:
- completed_order_items = OrderItem.objects.filter(order_id=completed_order)
- package_ids = completed_order_items.values_list("package_id", flat=True)
- influencers = Package.objects.filter(id__in=package_ids).values_list(
- "influencer_id", flat=True
- )
- influencer_ids.update(influencers)
-
- return list(influencer_ids)
-
- def get_is_twitter_connected(self, business_meta_data):
- userAccount = business_meta_data.user_account
- if userAccount.twitter_account:
- return True
- return False
-
-
- def get_is_wallet_connected(self, business_meta_data):
- user_wallet = Wallet.objects.filter(user_id = business_meta_data.user_account)
- if(user_wallet):
- return True
- return False
-
-
class CategoryMasterSerializer(serializers.ModelSerializer):
class Meta:
model = CategoryMaster
@@ -125,7 +85,7 @@ def get_user_id(self, twitter_account):
def get_service_types(self, twitter_account):
services = Service.objects.filter(
- package__influencer__twitter_account=twitter_account
+ package__influencer__twitter_account=twitter_account, deleted_at=None
)
# Extract service types and prices
@@ -268,6 +228,52 @@ def update(self, instance, validated_data):
return instance
+class BusinessAccountMetaDataSerializer(serializers.ModelSerializer):
+ influencer_ids = serializers.SerializerMethodField(read_only=True)
+ is_twitter_connected = serializers.SerializerMethodField(read_only=True)
+ is_wallet_connected = serializers.SerializerMethodField(read_only=True)
+ user_twitter_profile_image = serializers.SerializerMethodField(read_only=True)
+ user_account = UserSerializer(read_only=True)
+
+ class Meta:
+ model = BusinessAccountMetaData
+ fields = "__all__"
+
+ def get_influencer_ids(self, business_meta_data):
+ completed_orders = Order.objects.filter(
+ buyer=business_meta_data.user_account, status="completed"
+ )
+ influencer_ids = set()
+
+ for completed_order in completed_orders:
+ completed_order_items = OrderItem.objects.filter(order_id=completed_order)
+ package_ids = completed_order_items.values_list("package_id", flat=True)
+ influencers = Package.objects.filter(id__in=package_ids).values_list(
+ "influencer_id", flat=True
+ )
+ influencer_ids.update(influencers)
+
+ return list(influencer_ids)
+
+ def get_is_twitter_connected(self, business_meta_data):
+ userAccount = business_meta_data.user_account
+ if userAccount.twitter_account:
+ return True
+ return False
+
+
+ def get_is_wallet_connected(self, business_meta_data):
+ user_wallet = Wallet.objects.filter(user_id = business_meta_data.user_account)
+ if(user_wallet):
+ return True
+ return False
+
+ def get_user_twitter_profile_image(self, business_meta_data):
+ userAccount = business_meta_data.user_account
+ try:
+ return userAccount.twitter_account.profile_image_url
+ except Exception as e:
+ return ""
class TwitterReadSerializer(serializers.ModelSerializer):
class Meta:
diff --git a/src/api/marketplace/core/apps.py b/src/api/marketplace/core/apps.py
index 8115ae60..1ded3d15 100644
--- a/src/api/marketplace/core/apps.py
+++ b/src/api/marketplace/core/apps.py
@@ -4,3 +4,7 @@
class CoreConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'core'
+
+ def ready(self) -> None:
+ import core.signals
+ super().ready()
diff --git a/src/api/marketplace/core/services.py b/src/api/marketplace/core/services.py
index c7503049..6a604df1 100644
--- a/src/api/marketplace/core/services.py
+++ b/src/api/marketplace/core/services.py
@@ -2,7 +2,8 @@
import requests
from decouple import config
-
+from packages.models import Service
+from .models import Configuration
def get_twitter_usage():
url = "https://api.twitter.com/2/usage/tweets"
@@ -10,3 +11,10 @@ def get_twitter_usage():
response = requests.get(url, headers=headers)
response.raise_for_status()
return response.json()
+
+
+def update_priority_fees():
+ services = Service.objects.all()
+ current_platform_fees = int(
+ Configuration.objects.get(key="platform_fees").value)
+ services.update(platform_fees=current_platform_fees)
diff --git a/src/api/marketplace/core/signals.py b/src/api/marketplace/core/signals.py
new file mode 100644
index 00000000..ae7e02bd
--- /dev/null
+++ b/src/api/marketplace/core/signals.py
@@ -0,0 +1,14 @@
+# From the configuration model, whenever the key "priority_fees" is updated, I want to run a function
+# that will update the priority fees in the database.
+
+from django.db.models.signals import post_save
+from django.dispatch import receiver
+
+from .models import Configuration
+from .services import update_priority_fees
+
+
+@receiver(post_save, sender=Configuration)
+def post_save_update_priority_fees(sender, instance, created, **kwargs):
+ if instance.key == "platform_fees" and not created:
+ update_priority_fees()
diff --git a/src/ui/app/business/components/influencersContainer/influencersCards/index.tsx b/src/ui/app/business/components/influencersContainer/influencersCards/index.tsx
index 507f5690..d2f4ad6b 100644
--- a/src/ui/app/business/components/influencersContainer/influencersCards/index.tsx
+++ b/src/ui/app/business/components/influencersContainer/influencersCards/index.tsx
@@ -286,18 +286,18 @@ export default function InfluencersCards({
justifyContent={"center"}
alignItems={"center"}
>
-
+
Followers:
{influencer?.followers}
-
+
{`${influencer?.minPrice} - ${influencer?.maxPrice}`}
diff --git a/src/ui/app/business/explore/page.tsx b/src/ui/app/business/explore/page.tsx
index 17410970..73c5e91c 100644
--- a/src/ui/app/business/explore/page.tsx
+++ b/src/ui/app/business/explore/page.tsx
@@ -192,7 +192,7 @@ export default function Explore({}: Props) {
{pagination?.total_data_count ?? 0} Results
{/* The step should be in the database with the corresponding route */}
- {/* */}
+
0) {
+ if (data?.pagination?.total_data_count > 0) {
setHasAMessage(true);
}
}
diff --git a/src/ui/app/business/profile-preview/[id]/page.tsx b/src/ui/app/business/profile-preview/[id]/page.tsx
index f6d360a4..7eedbec9 100644
--- a/src/ui/app/business/profile-preview/[id]/page.tsx
+++ b/src/ui/app/business/profile-preview/[id]/page.tsx
@@ -27,6 +27,7 @@ import Image from "next/image";
import NotFound from "@/public/svg/not_found.svg";
import { stringToColor } from "@/src/utils/helper";
import { BADGES } from "@/src/utils/consts";
+import { getProfileCompletedStatus } from "@/src/services/profileCompletion";
type Props = {
params: {
@@ -48,22 +49,6 @@ const styles = {
},
};
-const getProfileCompletedStatus: (businessDetails: any) => string = (
- businessDetails
-) => {
- if (businessDetails) {
- let count = 0;
- if (businessDetails?.isTwitterAccountConnected) count += 5;
- if (businessDetails?.isWalletConnected) count += 5;
- count +=
- Object.values(businessDetails).filter(
- (value) => value !== "" && value !== null
- ).length - 7;
- return `${count} / ${10 + Object.keys(businessDetails).length - 7}`;
- }
- return "-";
-};
-
const formatTwitterFollowers = (followersCount: any) => {
if (followersCount >= 1000000) {
// Convert to millions format
@@ -80,8 +65,7 @@ const formatTwitterFollowers = (followersCount: any) => {
export default function BusinessProfilePreview({ params }: Props) {
const [isProfileComplete, setIsProfileComplete] = useState(false);
const [collaborations, setCollaborations] = useState([]);
- const [businessDetails, setBusinessDetails] =
- useState();
+ const [businessDetails, setBusinessDetails] = useState();
const user = useAppSelector((state) => state.user?.user);
const router = useRouter();
@@ -104,6 +88,8 @@ export default function BusinessProfilePreview({ params }: Props) {
.replace(/\s/g, "")
.split("/");
+ console.log(completionStringArr);
+
return (
(parseInt(completionStringArr[0]) / parseInt(completionStringArr[1])) *
100
@@ -160,8 +146,6 @@ export default function BusinessProfilePreview({ params }: Props) {
if (isSuccess) {
setBusinessDetails({
...data?.data,
- isTwitterAccountConnected: !!user?.twitter_account,
- isWalletConnected: !!user?.wallets?.length,
});
getCollaborators(data?.data?.influencer_ids);
}
@@ -211,10 +195,10 @@ export default function BusinessProfilePreview({ params }: Props) {
alignItems: "center",
}}
>
- {user?.twitter_account?.profile_image_url ? (
+ {!!businessDetails?.user_twitter_profile_image ? (
) : (
)}
@@ -412,7 +398,7 @@ export default function BusinessProfilePreview({ params }: Props) {
margin: "20px",
}}
>
- {user?.role?.name === "business_owner" ? (
+ {user?.id == params?.id ? (
- {user?.role?.name === "business_owner" ? (
+ {user?.id == params?.id ? (
isProfileComplete ? (
Profile complete! Enjoy your upgraded badge & enhanced
diff --git a/src/ui/app/influencer/dashboard/page.tsx b/src/ui/app/influencer/dashboard/page.tsx
index 1353a047..26efdb8e 100644
--- a/src/ui/app/influencer/dashboard/page.tsx
+++ b/src/ui/app/influencer/dashboard/page.tsx
@@ -56,6 +56,7 @@ import Joyride, { ACTIONS, EVENTS, STATUS } from "react-joyride";
import XfluencerLogo from "@/public/svg/Xfluencer_Logo_Beta.svg";
import { DriveEta } from "@mui/icons-material";
import WalletConnectModal from "@/src/components/web3Components/walletConnectModal";
+import { getProfileCompletedStatus } from "@/src/services/profileCompletion";
const tabs = [
{
@@ -72,22 +73,6 @@ const tabs = [
},
];
-const getProfileCompletedStatus: (businessDetails: any) => string = (
- businessDetails
-) => {
- if (businessDetails) {
- let count = 0;
- if (businessDetails?.isTwitterAccountConnected) count += 5;
- if (businessDetails?.isWalletConnected) count += 5;
- count +=
- Object.values(businessDetails).filter(
- (value) => value !== "" && value !== null
- ).length - 7;
- return `${count} / ${10 + Object.keys(businessDetails).length - 7}`;
- }
- return "-";
-};
-
export default function BusinessDashboardPage() {
const router = useRouter();
const searchParams = useSearchParams();
@@ -640,8 +625,7 @@ export default function BusinessDashboardPage() {
)
.replace(/\s/g, "")
.split("/");
-
- return (
+ return (
(parseInt(completionStringArr[0]) / parseInt(completionStringArr[1])) *
100
);
@@ -1104,6 +1088,7 @@ export default function BusinessDashboardPage() {
{params?.row?.approved &&
(params?.row?.status === ORDER_ITEM_STATUS.ACCEPTED ||
params?.row?.status === ORDER_ITEM_STATUS.CANCELLED) &&
+ params?.row?.order_id?.status === ORDER_STATUS.ACCEPTED &&
// Publish date is in the future
dayjs(params?.row?.publish_date) > dayjs() && (
diff --git a/src/ui/app/influencer/messages/page.tsx b/src/ui/app/influencer/messages/page.tsx
index d9e0c0fb..5255e1a0 100644
--- a/src/ui/app/influencer/messages/page.tsx
+++ b/src/ui/app/influencer/messages/page.tsx
@@ -155,11 +155,11 @@ export default function BusinessMessages() {
);
if (isSuccess) {
// Fetching all user-message and if there's exactly 1 object, show the user guide
- if (data?.data?.orders?.length == 1) {
+ if (data?.pagination?.total_data_count == 1) {
setStepIndex(0);
setRun(true);
}
- if (data?.data?.orders?.length > 0) {
+ if (data?.pagination?.total_data_count > 0) {
setHasAMessage(true);
}
}
diff --git a/src/ui/app/influencer/orders/page.tsx b/src/ui/app/influencer/orders/page.tsx
index 1bb0d290..16ca9011 100644
--- a/src/ui/app/influencer/orders/page.tsx
+++ b/src/ui/app/influencer/orders/page.tsx
@@ -55,7 +55,6 @@ export default function Orders() {
});
const [open, setOpen] = React.useState(false);
const [loading, setLoading] = useState(false);
- const [redirectLoading, setRedirectLoading] = useState(false);
const [actionLoading, setActionLoading] = useState(false);
const [orders, setOrders] = useState([]);
const [rowSelectionModel, setRowSelectionModel] =
@@ -217,16 +216,6 @@ export default function Orders() {
total_data_count: data?.pagination?.total_data_count,
total_page_count: data?.pagination?.total_page_count,
});
-
- // Redirecting to dashboard page after two seconds if there are no orders pending
- if (data?.data?.orders.length == 0) {
- setRedirectLoading(true);
- setTimeout(() => {
- router.push("/influencer/dashboard/?tab=orders");
- }, 2000);
- } else {
- setRedirectLoading(false);
- }
} else {
notification(message ? message : "Something went wrong", "error");
}
@@ -474,6 +463,10 @@ export default function Orders() {
selectedAction.status == "Accept" ? "accepted" : "rejected"
}`
);
+ // Redirect to the dashboard if it's the last accepted order
+ if (orders?.length <= 1) {
+ router.push("/influencer/dashboard/?tab=orders");
+ }
} else {
notification(
message
@@ -730,26 +723,6 @@ export default function Orders() {
}}
locale={{ last: "Finish" }}
/>
- {redirectLoading ? (
- theme.zIndex.drawer + 1 }}
- open={true}
- >
-
-
-
- Redirecting to dashboard...
-
-
-
- ) : null}
);
}
diff --git a/src/ui/app/influencer/profile/[id]/_services/index.tsx b/src/ui/app/influencer/profile/[id]/_services/index.tsx
index 84c11186..8ca824e0 100644
--- a/src/ui/app/influencer/profile/[id]/_services/index.tsx
+++ b/src/ui/app/influencer/profile/[id]/_services/index.tsx
@@ -37,7 +37,7 @@ const Services = ({
const dispatch = useAppDispatch();
const cart = useAppSelector((state) => state.cart);
const loggedInUser = useAppSelector((state) => state.user?.user);
- const [type, setType] = React.useState(null);
+ const [type, setType] = React.useState("published");
const [services, setServices] = React.useState([]);
const [pagination, setPagination] = React.useState({
total_data_count: 0,
@@ -58,16 +58,13 @@ const Services = ({
const getServices = async () => {
try {
setLoading(true);
- const { message, data, isSuccess, errors } = await getService(
+ const { message, data, isSuccess } = await getService(
"packages/service",
{
page_number: pagination.current_page_number,
- page_size:
- id === loggedInUser?.id
- ? pagination.current_page_size
- : pagination.current_page_size + 1,
+ page_size: pagination.current_page_size,
influencer: id,
- status: id === loggedInUser?.id ? type : "published",
+ status: type,
}
);
if (isSuccess) {
@@ -120,6 +117,12 @@ const Services = ({
}
}, [refreshPage]);
+ useEffect(() => {
+ if (id && loggedInUser && loggedInUser?.id && loggedInUser?.id === id) {
+ setType(null);
+ }
+ }, [loggedInUser, id]);
+
useEffect(() => {
getServices();
}, [pagination.current_page_number, pagination.current_page_size, type]);
diff --git a/src/ui/src/components/dashboardComponents/orderSummaryDetails/index.tsx b/src/ui/src/components/dashboardComponents/orderSummaryDetails/index.tsx
index af76c764..207c34ba 100644
--- a/src/ui/src/components/dashboardComponents/orderSummaryDetails/index.tsx
+++ b/src/ui/src/components/dashboardComponents/orderSummaryDetails/index.tsx
@@ -58,6 +58,7 @@ const ContentTypeComponent = ({ meta_data }: { meta_data: any }) => {
display: "flex",
justifyContent: "space-between",
width: "100%",
+ alignItems: "center",
}}
>
{
target="_blank"
rel="noopener noreferrer"
underline="hover"
- sx={{ color: "#676767" }}
+ sx={{
+ color: "#676767",
+ width: "100%",
+ overflow: "hidden",
+ textOverflow: "ellipsis",
+ }}
>
{meta_data.value}
diff --git a/src/ui/src/components/profileComponents/createUpdateService/index.tsx b/src/ui/src/components/profileComponents/createUpdateService/index.tsx
index c427ba9e..5ce0c082 100644
--- a/src/ui/src/components/profileComponents/createUpdateService/index.tsx
+++ b/src/ui/src/components/profileComponents/createUpdateService/index.tsx
@@ -539,14 +539,14 @@ const CreateUpdateService = ({
>
- Platform Fee{" "}
- ({platform_fees}%)
-
+ Platform Fee
+ {" "}
+ ({platform_fees}%)
- Free only for beta
+ Free for closed beta
diff --git a/src/ui/src/components/shared/footer/index.tsx b/src/ui/src/components/shared/footer/index.tsx
index a4404e59..6f7b9835 100644
--- a/src/ui/src/components/shared/footer/index.tsx
+++ b/src/ui/src/components/shared/footer/index.tsx
@@ -1,5 +1,5 @@
import React from "react";
-import { Box, Grid, Typography } from "@mui/material";
+import { Box, Grid, Link, Typography } from "@mui/material";
import Image from "next/image";
import Facebook from "@/public/svg/Facebook.svg";
import Instgram from "@/public/svg/Instgram.svg";
@@ -63,11 +63,23 @@ const Footer = () => {
Company
- {["Blog", "Privacy Policy", "Xfluencer Pro (coming soon)"].map(
- (link) => (
- {link}
- )
- )}
+ Blog
+
+ Docs
+
+ {["Privacy Policy", "Xfluencer Pro (coming soon)"].map((link) => (
+ {link}
+ ))}
{/** Column 4 */}
diff --git a/src/ui/src/services/profileCompletion.ts b/src/ui/src/services/profileCompletion.ts
new file mode 100644
index 00000000..7eea2f6c
--- /dev/null
+++ b/src/ui/src/services/profileCompletion.ts
@@ -0,0 +1,37 @@
+export const getProfileCompletedStatus = (businessDetails: any): string => {
+ if (!businessDetails) {
+ return "-";
+ }
+
+ let count = 0;
+
+ const fields = [
+ { key: 'is_twitter_connected', weight: 5 },
+ { key: 'is_wallet_connected', weight: 5 },
+ 'business_name',
+ 'industry',
+ 'founded',
+ 'headquarters',
+ 'bio',
+ 'user_email',
+ 'phone',
+ 'website',
+ 'twitter_account',
+ 'linked_in',
+ ];
+
+ fields.forEach((field) => {
+ if (typeof field === 'string') {
+ if (!!businessDetails[field]) {
+ count++;
+ }
+ } else {
+ if (businessDetails[field.key]) {
+ count += field.weight;
+ }
+ }
+ });
+ console.log(count)
+ return `${count} / 20`;
+ };
+
\ No newline at end of file