diff --git a/.DS_Store b/.DS_Store index bc6562e..58f0955 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/.gitea/workflows/deploy-main.yml b/.gitea/workflows/deploy-main.yml new file mode 100644 index 0000000..c6d1b47 --- /dev/null +++ b/.gitea/workflows/deploy-main.yml @@ -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 diff --git a/app/.DS_Store b/app/.DS_Store index a4b4205..1ff97b5 100644 Binary files a/app/.DS_Store and b/app/.DS_Store differ diff --git a/client/src/components/trans-routes/PersonnelInfoTable.js b/client/src/components/trans-routes/PersonnelInfoTable.js index fe42a56..5beb3e3 100644 --- a/client/src/components/trans-routes/PersonnelInfoTable.js +++ b/client/src/components/trans-routes/PersonnelInfoTable.js @@ -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 => diff --git a/client/src/components/trans-routes/PersonnelSection.js b/client/src/components/trans-routes/PersonnelSection.js index 9706da4..09ae70c 100644 --- a/client/src/components/trans-routes/PersonnelSection.js +++ b/client/src/components/trans-routes/PersonnelSection.js @@ -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} /> diff --git a/client/src/components/trans-routes/RouteCustomerEditor.js b/client/src/components/trans-routes/RouteCustomerEditor.js index 7d2c231..59b31af 100644 --- a/client/src/components/trans-routes/RouteCustomerEditor.js +++ b/client/src/components/trans-routes/RouteCustomerEditor.js @@ -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 ( -