This commit is contained in:
101
.gitea/workflows/deploy-main.yml
Normal file
101
.gitea/workflows/deploy-main.yml
Normal file
@@ -0,0 +1,101 @@
|
||||
name: Build And Deploy Main
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
build-and-deploy:
|
||||
runs-on:
|
||||
- self-hosted
|
||||
- mayo-cicd
|
||||
env:
|
||||
PROJECT_DIR: ~/temp/tspt
|
||||
REMOTE_HOST: 34.232.175.208
|
||||
REMOTE_USER: rocky
|
||||
SSH_KEY: ~/ssh-certs/charlie_ws.pem
|
||||
REMOTE_APP_ROOT: /www/wwwroot/worldshine1
|
||||
REMOTE_VIEWS_DIR: /www/wwwroot/worldshine1/app/views
|
||||
EXCLUDE_PATTERN: (^|/)\.DS_Store$|(^|/)_MACOSX(/|$)
|
||||
BEFORE_SHA: ${{ github.event.before }}
|
||||
AFTER_SHA: ${{ github.sha }}
|
||||
steps:
|
||||
- name: Validate project checkout
|
||||
run: |
|
||||
set -e
|
||||
if [ ! -d "$PROJECT_DIR/.git" ]; then
|
||||
echo "Expected git repository at $PROJECT_DIR but not found."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Pull latest main
|
||||
run: |
|
||||
set -e
|
||||
cd "$PROJECT_DIR"
|
||||
git fetch origin main
|
||||
git checkout main
|
||||
git pull origin main
|
||||
|
||||
- name: Build client
|
||||
run: |
|
||||
set -e
|
||||
cd "$PROJECT_DIR/client"
|
||||
npm install
|
||||
npm run build
|
||||
|
||||
- name: Replace local app views from client dist
|
||||
run: |
|
||||
set -e
|
||||
cd "$PROJECT_DIR"
|
||||
mkdir -p app/views
|
||||
rm -rf app/views/*
|
||||
rsync -a --delete --exclude ".DS_Store" --exclude "_MACOSX" client/dist/ app/views/
|
||||
|
||||
- name: Deploy views to remote server
|
||||
run: |
|
||||
set -e
|
||||
cd "$PROJECT_DIR"
|
||||
SSH_CMD="ssh -i $SSH_KEY -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"
|
||||
RSYNC_RSH="ssh -i $SSH_KEY -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"
|
||||
$SSH_CMD "$REMOTE_USER@$REMOTE_HOST" "mkdir -p \"$REMOTE_VIEWS_DIR\""
|
||||
rsync -az --delete --exclude ".DS_Store" --exclude "_MACOSX" -e "$RSYNC_RSH" app/views/ "$REMOTE_USER@$REMOTE_HOST:$REMOTE_VIEWS_DIR/"
|
||||
|
||||
- name: Deploy changed backend files only
|
||||
run: |
|
||||
set -e
|
||||
cd "$PROJECT_DIR"
|
||||
SSH_CMD="ssh -i $SSH_KEY -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"
|
||||
RSYNC_RSH="ssh -i $SSH_KEY -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"
|
||||
CHANGE_PATHS=(app/controllers app/middlewares app/models app/routes app/scheduler app/services)
|
||||
|
||||
if [ -n "$BEFORE_SHA" ] && [ "$BEFORE_SHA" != "0000000000000000000000000000000000000000" ]; then
|
||||
CHANGED_FILES=$(git diff --name-only "$BEFORE_SHA" "$AFTER_SHA" -- "${CHANGE_PATHS[@]}" | rg -v "$EXCLUDE_PATTERN" || true)
|
||||
DELETED_FILES=$(git diff --name-only --diff-filter=D "$BEFORE_SHA" "$AFTER_SHA" -- "${CHANGE_PATHS[@]}" | rg -v "$EXCLUDE_PATTERN" || true)
|
||||
else
|
||||
CHANGED_FILES=$(git show --name-only --pretty="" "$AFTER_SHA" -- "${CHANGE_PATHS[@]}" | rg -v "$EXCLUDE_PATTERN" || true)
|
||||
DELETED_FILES=""
|
||||
fi
|
||||
|
||||
if [ -z "$CHANGED_FILES" ]; then
|
||||
echo "No backend file changes detected in target folders."
|
||||
else
|
||||
while IFS= read -r file; do
|
||||
[ -z "$file" ] && continue
|
||||
[ -f "$file" ] || continue
|
||||
remote_file="$REMOTE_APP_ROOT/$file"
|
||||
remote_dir=$(dirname "$remote_file")
|
||||
$SSH_CMD "$REMOTE_USER@$REMOTE_HOST" "mkdir -p \"$remote_dir\""
|
||||
rsync -az --exclude ".DS_Store" --exclude "_MACOSX" -e "$RSYNC_RSH" "$file" "$REMOTE_USER@$REMOTE_HOST:$remote_file"
|
||||
echo "Deployed: $file"
|
||||
done <<< "$CHANGED_FILES"
|
||||
fi
|
||||
|
||||
if [ -n "$DELETED_FILES" ]; then
|
||||
while IFS= read -r file; do
|
||||
[ -z "$file" ] && continue
|
||||
remote_file="$REMOTE_APP_ROOT/$file"
|
||||
$SSH_CMD "$REMOTE_USER@$REMOTE_HOST" "rm -f \"$remote_file\""
|
||||
echo "Deleted on remote: $file"
|
||||
done <<< "$DELETED_FILES"
|
||||
fi
|
||||
BIN
app/.DS_Store
vendored
BIN
app/.DS_Store
vendored
Binary file not shown.
@@ -15,7 +15,8 @@ const PersonnelInfoTable = ({transRoutes, showCompletedInfo,
|
||||
driverName, vehicle, relatedOutbound,
|
||||
vehicles, isInbound, deleteFile,
|
||||
keyword, statusFilter, customerNameFilter,
|
||||
customerTableId, routeTypeFilter, customerTypeFilter
|
||||
customerTableId, routeTypeFilter, customerTypeFilter,
|
||||
onRouteUpdated = null
|
||||
}) => {
|
||||
const [show, setShow] = useState(false);
|
||||
const [showGroupEditor, setShowGroupEditor] = useState(false);
|
||||
@@ -223,6 +224,9 @@ const PersonnelInfoTable = ({transRoutes, showCompletedInfo,
|
||||
if (dateStr !== '' && dateStr !== moment().format('MM/DD/YYYY')) {
|
||||
finalParams = Object.assign({}, finalParams, {dateText: dateStr})
|
||||
}
|
||||
if (onRouteUpdated) {
|
||||
finalParams = Object.assign({}, finalParams, {callback: onRouteUpdated})
|
||||
}
|
||||
dispatch(updateRoute(finalParams));
|
||||
// if (removeSignature && deleteFile) {
|
||||
// deleteFile();
|
||||
@@ -276,6 +280,9 @@ const PersonnelInfoTable = ({transRoutes, showCompletedInfo,
|
||||
if (dateStr !== '' && dateStr !== moment().format('MM/DD/YYYY')) {
|
||||
finalParams = Object.assign({}, finalParams, {dateText: dateStr})
|
||||
}
|
||||
if (onRouteUpdated) {
|
||||
finalParams = Object.assign({}, finalParams, {callback: onRouteUpdated})
|
||||
}
|
||||
dispatch(updateRoute(finalParams));
|
||||
// if (removeSignature && deleteFile) {
|
||||
// deleteFile();
|
||||
@@ -628,6 +635,9 @@ const PersonnelInfoTable = ({transRoutes, showCompletedInfo,
|
||||
if (dateStr !== '' && dateStr !== moment().format('MM/DD/YYYY')) {
|
||||
finalParams = Object.assign({}, finalParams, {dateText: dateStr})
|
||||
}
|
||||
if (onRouteUpdated) {
|
||||
finalParams = Object.assign({}, finalParams, {callback: onRouteUpdated})
|
||||
}
|
||||
|
||||
// Log final payload before dispatch, especially for Wang,Huanran
|
||||
const wangCustomer = finalParams.data.route_customer_list?.find(c =>
|
||||
|
||||
@@ -5,7 +5,8 @@ const PersonnelSection = ({transRoutes, sectionName, showCompletedInfo,
|
||||
showGroupInfo, allowForceEdit, showFilter,
|
||||
driverName, vehicle, relatedOutbound,
|
||||
vehicles, isInbound, deleteFile, keyword,
|
||||
statusFilter, customerTypeFilter, customerNameFilter, customerTableId, routeTypeFilter
|
||||
statusFilter, customerTypeFilter, customerNameFilter, customerTableId, routeTypeFilter,
|
||||
onRouteUpdated = null
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
@@ -21,6 +22,7 @@ const PersonnelSection = ({transRoutes, sectionName, showCompletedInfo,
|
||||
customerNameFilter={customerNameFilter}
|
||||
customerTable={customerTableId}
|
||||
routeTypeFilter={routeTypeFilter}
|
||||
onRouteUpdated={onRouteUpdated}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -16,11 +16,13 @@ const ItemTypes = {
|
||||
|
||||
const Card = ({ content, index, moveCard }) => {
|
||||
const ref = useRef(null);
|
||||
const [{ handlerId }, drop] = useDrop({
|
||||
const [{ handlerId, isOver, draggedIndex }, drop] = useDrop({
|
||||
accept: ItemTypes.CARD,
|
||||
collect(monitor) {
|
||||
return {
|
||||
handlerId: monitor.getHandlerId(),
|
||||
isOver: monitor.isOver({ shallow: true }),
|
||||
draggedIndex: monitor.getItem()?.index,
|
||||
}
|
||||
},
|
||||
drop(item, monitor) {
|
||||
@@ -72,9 +74,21 @@ const Card = ({ content, index, moveCard }) => {
|
||||
}),
|
||||
})
|
||||
const opacity = isDragging ? 0 : 1
|
||||
const showDropTopIndicator = isOver && draggedIndex !== undefined && draggedIndex > index;
|
||||
const showDropBottomIndicator = isOver && draggedIndex !== undefined && draggedIndex < index;
|
||||
const dropZoneStyle = {
|
||||
opacity,
|
||||
paddingTop: '10px',
|
||||
paddingBottom: '10px',
|
||||
borderRadius: '8px',
|
||||
backgroundColor: isOver ? '#eef6ff' : 'transparent',
|
||||
borderTop: showDropTopIndicator ? '4px solid #0d6efd' : '4px solid transparent',
|
||||
borderBottom: showDropBottomIndicator ? '4px solid #0d6efd' : '4px solid transparent',
|
||||
transition: 'background-color 0.12s ease, border-color 0.12s ease'
|
||||
};
|
||||
drag(drop(ref))
|
||||
return (
|
||||
<div ref={ref} style={{ opacity }} data-handler-id={handlerId}>
|
||||
<div ref={ref} style={dropZoneStyle} data-handler-id={handlerId}>
|
||||
{content}
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -26,6 +26,7 @@ const RouteView = () => {
|
||||
const [signatureRequest, setSignatureRequest] = useState(undefined);
|
||||
const [routeStatusValue, setRouteStatusValue] = useState('');
|
||||
const [isSavingRouteStatus, setIsSavingRouteStatus] = useState(false);
|
||||
const [latestRouteForStatus, setLatestRouteForStatus] = useState(undefined);
|
||||
const paramsQuery = new URLSearchParams(window.location.search);
|
||||
const scheduleDate = paramsQuery.get('dateSchedule');
|
||||
|
||||
@@ -40,6 +41,7 @@ const RouteView = () => {
|
||||
{ value: ROUTE_STATUS.SIGN_OFF, label: 'Signed Off' },
|
||||
{ value: ROUTE_STATUS.UNEXPECTED_ABSENT, label: 'Unexpected Absent' },
|
||||
];
|
||||
const routeForStatusView = latestRouteForStatus || currentRoute;
|
||||
const closeModal = () => {
|
||||
setShowVehicleDetails(false);
|
||||
}
|
||||
@@ -109,11 +111,27 @@ const RouteView = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const refreshRouteStatusData = async () => {
|
||||
if (!currentRoute?.id) return;
|
||||
try {
|
||||
const response = await TransRoutesService.getRoute(currentRoute.id);
|
||||
if (response?.data) {
|
||||
setLatestRouteForStatus(response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error refreshing route status data:', error);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const currentStatus = Array.isArray(currentRoute?.status) ? currentRoute.status[0] : '';
|
||||
setRouteStatusValue(currentStatus || '');
|
||||
}, [currentRoute?.id, currentRoute?.status]);
|
||||
|
||||
useEffect(() => {
|
||||
setLatestRouteForStatus(undefined);
|
||||
}, [currentRoute?.id]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!currentRoute?.driver || currentDriver?.id) {
|
||||
setFallbackDriver(undefined);
|
||||
@@ -268,7 +286,7 @@ const RouteView = () => {
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-md-12 mb-4">
|
||||
{currentRoute && <PersonnelSection transRoutes={[currentRoute]} showCompletedInfo={true} showGroupInfo={true} isInbound={currentRoute?.type === 'inbound' } allowForceEdit={AuthService.canViewRoutes()} sectionName="Personnel Status (click on each user to edit)" relatedOutbound={getRelatedOutboundRoutesForThisView()} vehicle={currentVehicle} driverName={resolvedDriverName} deleteFile={deleteFile}/>}
|
||||
{routeForStatusView && <PersonnelSection transRoutes={[routeForStatusView]} showCompletedInfo={true} showGroupInfo={true} isInbound={routeForStatusView?.type === 'inbound' } allowForceEdit={AuthService.canViewRoutes()} sectionName="Personnel Status (click on each user to edit)" relatedOutbound={getRelatedOutboundRoutesForThisView()} vehicle={currentVehicle} driverName={resolvedDriverName} deleteFile={deleteFile} onRouteUpdated={refreshRouteStatusData}/>}
|
||||
</div>
|
||||
</div>
|
||||
</Tab>
|
||||
|
||||
Reference in New Issue
Block a user